Форматирование
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
csharp_prefer_braces = true:warning
|
||||||
|
csharp_style_namespace_declarations = file_scoped:warning
|
||||||
|
dotnet_diagnostic.IDE0011.severity = warning
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace FormatFixer
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var srcPath = @"e:\GIT\forkmessager\apps\server-net\src";
|
||||||
|
var files = Directory.GetFiles(srcPath, "*.cs", SearchOption.AllDirectories);
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines(file);
|
||||||
|
bool changed = false;
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
var stripped = line.TrimStart();
|
||||||
|
if ((stripped.StartsWith("if (") || stripped.StartsWith("if(") ||
|
||||||
|
stripped.StartsWith("foreach (") || stripped.StartsWith("foreach("))
|
||||||
|
&& stripped.EndsWith(";"))
|
||||||
|
{
|
||||||
|
// Check if it's already a block
|
||||||
|
if (stripped.Contains("{")) continue;
|
||||||
|
|
||||||
|
int parenCount = 0;
|
||||||
|
bool inString = false;
|
||||||
|
int firstParenIdx = line.IndexOf('(');
|
||||||
|
int condEndIdx = -1;
|
||||||
|
|
||||||
|
for (int j = firstParenIdx; j < line.Length; j++)
|
||||||
|
{
|
||||||
|
char c = line[j];
|
||||||
|
if (c == '"' && line[j - 1] != '\\')
|
||||||
|
{
|
||||||
|
inString = !inString;
|
||||||
|
}
|
||||||
|
else if (!inString)
|
||||||
|
{
|
||||||
|
if (c == '(') parenCount++;
|
||||||
|
else if (c == ')')
|
||||||
|
{
|
||||||
|
parenCount--;
|
||||||
|
if (parenCount == 0)
|
||||||
|
{
|
||||||
|
condEndIdx = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condEndIdx != -1)
|
||||||
|
{
|
||||||
|
var statement = line.Substring(condEndIdx + 1).Trim();
|
||||||
|
if (!string.IsNullOrEmpty(statement) && !statement.StartsWith("{") && statement.EndsWith(";"))
|
||||||
|
{
|
||||||
|
var indent = line.Substring(0, line.Length - line.TrimStart().Length);
|
||||||
|
var newLine1 = line.Substring(0, condEndIdx + 1);
|
||||||
|
var newLine2 = indent + "{";
|
||||||
|
var newLine3 = indent + " " + statement;
|
||||||
|
var newLine4 = indent + "}";
|
||||||
|
|
||||||
|
lines[i] = newLine1 + Environment.NewLine + newLine2 + Environment.NewLine + newLine3 + Environment.NewLine + newLine4;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
File.WriteAllText(file, string.Join(Environment.NewLine, lines));
|
||||||
|
Console.WriteLine("Fixed: " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine("Formatting complete.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -57,9 +57,18 @@ internal sealed class CleanRunCommandHandler : ICommandHandler<CleanRunCommand,
|
|||||||
.Where(u => !string.IsNullOrEmpty(u.Avatar))
|
.Where(u => !string.IsNullOrEmpty(u.Avatar))
|
||||||
.Select(u => u.Avatar!);
|
.Select(u => u.Avatar!);
|
||||||
|
|
||||||
foreach (var u in activeMessageUrls) validUrls.Add(u!);
|
foreach (var u in activeMessageUrls)
|
||||||
foreach (var u in activeChatUrls) validUrls.Add(u);
|
{
|
||||||
foreach (var u in activeUserUrls) validUrls.Add(u);
|
validUrls.Add(u!);
|
||||||
|
}
|
||||||
|
foreach (var u in activeChatUrls)
|
||||||
|
{
|
||||||
|
validUrls.Add(u);
|
||||||
|
}
|
||||||
|
foreach (var u in activeUserUrls)
|
||||||
|
{
|
||||||
|
validUrls.Add(u);
|
||||||
|
}
|
||||||
|
|
||||||
var validFileIds = validUrls
|
var validFileIds = validUrls
|
||||||
.Where(u => u.Contains("/api/files/"))
|
.Where(u => u.Contains("/api/files/"))
|
||||||
|
|||||||
+4
-1
@@ -26,7 +26,10 @@ internal sealed class ResetUserPasswordCommandHandler : ICommandHandler<ResetUse
|
|||||||
public async Task<Result<SuccessResponse>> Handle(ResetUserPasswordCommand request, CancellationToken cancellationToken)
|
public async Task<Result<SuccessResponse>> Handle(ResetUserPasswordCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<SuccessResponse>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<SuccessResponse>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(request.NewPassword))
|
if (string.IsNullOrWhiteSpace(request.NewPassword))
|
||||||
return Result.Failure<SuccessResponse>(new Error("InvalidPassword", "Password cannot be empty"));
|
return Result.Failure<SuccessResponse>(new Error("InvalidPassword", "Password cannot be empty"));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -60,9 +60,18 @@ internal sealed class CleanDryRunQueryHandler : IQueryHandler<CleanDryRunQuery,
|
|||||||
.Where(u => !string.IsNullOrEmpty(u.Avatar))
|
.Where(u => !string.IsNullOrEmpty(u.Avatar))
|
||||||
.Select(u => u.Avatar!);
|
.Select(u => u.Avatar!);
|
||||||
|
|
||||||
foreach (var u in activeMessageUrls) validUrls.Add(u!);
|
foreach (var u in activeMessageUrls)
|
||||||
foreach (var u in activeChatUrls) validUrls.Add(u);
|
{
|
||||||
foreach (var u in activeUserUrls) validUrls.Add(u);
|
validUrls.Add(u!);
|
||||||
|
}
|
||||||
|
foreach (var u in activeChatUrls)
|
||||||
|
{
|
||||||
|
validUrls.Add(u);
|
||||||
|
}
|
||||||
|
foreach (var u in activeUserUrls)
|
||||||
|
{
|
||||||
|
validUrls.Add(u);
|
||||||
|
}
|
||||||
|
|
||||||
var validFileIds = validUrls
|
var validFileIds = validUrls
|
||||||
.Where(u => u.Contains("/api/files/"))
|
.Where(u => u.Contains("/api/files/"))
|
||||||
|
|||||||
+5
-2
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -27,7 +27,10 @@ internal sealed class GetUserDetailsQueryHandler : IQueryHandler<GetUserDetailsQ
|
|||||||
public async Task<Result<AdminUserDetailsDto>> Handle(GetUserDetailsQuery request, CancellationToken cancellationToken)
|
public async Task<Result<AdminUserDetailsDto>> Handle(GetUserDetailsQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var targetUser = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var targetUser = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (targetUser == null) return Result.Failure<AdminUserDetailsDto>(new Error("User.NotFound", "User not found"));
|
if (targetUser == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<AdminUserDetailsDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var messagesCount = await _chatsDbContext.Messages.CountAsync(m => m.SenderId == request.UserId, cancellationToken);
|
var messagesCount = await _chatsDbContext.Messages.CountAsync(m => m.SenderId == request.UserId, cancellationToken);
|
||||||
|
|
||||||
|
|||||||
+17
-5
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -47,12 +47,18 @@ internal sealed class AddStoryReactionCommandHandler : ICommandHandler<AddStoryR
|
|||||||
public async Task<Result<MessageResponse>> Handle(AddStoryReactionCommand request, CancellationToken cancellationToken)
|
public async Task<Result<MessageResponse>> Handle(AddStoryReactionCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
||||||
if (story == null) return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var existing = await _context.StoryReactions
|
var existing = await _context.StoryReactions
|
||||||
.FirstOrDefaultAsync(r => r.StoryId == request.StoryId && r.UserId == request.UserId && r.Emoji == request.Emoji, cancellationToken);
|
.FirstOrDefaultAsync(r => r.StoryId == request.StoryId && r.UserId == request.UserId && r.Emoji == request.Emoji, cancellationToken);
|
||||||
|
|
||||||
if (existing != null) return Result.Success(new MessageResponse("Reaction already exists"));
|
if (existing != null)
|
||||||
|
{
|
||||||
|
return Result.Success(new MessageResponse("Reaction already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
var reaction = new StoryReaction(request.StoryId, request.UserId, request.Emoji);
|
var reaction = new StoryReaction(request.StoryId, request.UserId, request.Emoji);
|
||||||
_context.StoryReactions.Add(reaction);
|
_context.StoryReactions.Add(reaction);
|
||||||
@@ -103,7 +109,10 @@ internal sealed class AddStoryReactionCommandHandler : ICommandHandler<AddStoryR
|
|||||||
|
|
||||||
private string GetStoryQuote(Story story)
|
private string GetStoryQuote(Story story)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(story.Content)) return story.Content;
|
if (!string.IsNullOrEmpty(story.Content))
|
||||||
|
{
|
||||||
|
return story.Content;
|
||||||
|
}
|
||||||
return story.Type.ToLower() switch
|
return story.Type.ToLower() switch
|
||||||
{
|
{
|
||||||
"image" => "🖼 Фото",
|
"image" => "🖼 Фото",
|
||||||
@@ -119,7 +128,10 @@ internal sealed class AddStoryReactionCommandHandler : ICommandHandler<AddStoryR
|
|||||||
c.Type == ChatType.Personal &&
|
c.Type == ChatType.Personal &&
|
||||||
c.Members.Any(m => m.UserId == userId2));
|
c.Members.Any(m => m.UserId == userId2));
|
||||||
|
|
||||||
if (personalChat != null) return personalChat.Id;
|
if (personalChat != null)
|
||||||
|
{
|
||||||
|
return personalChat.Id;
|
||||||
|
}
|
||||||
|
|
||||||
var command = new Knot.Modules.Chats.Application.Chats.Create.CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { userId1, userId2 });
|
var command = new Knot.Modules.Chats.Application.Chats.Create.CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { userId1, userId2 });
|
||||||
var result = await _sender.Send(command, cancellationToken);
|
var result = await _sender.Send(command, cancellationToken);
|
||||||
|
|||||||
+13
-4
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -47,7 +47,10 @@ internal sealed class AddStoryReplyCommandHandler : ICommandHandler<AddStoryRepl
|
|||||||
public async Task<Result<MessageResponse>> Handle(AddStoryReplyCommand request, CancellationToken cancellationToken)
|
public async Task<Result<MessageResponse>> Handle(AddStoryReplyCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
||||||
if (story == null) return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var reply = new StoryReply(request.StoryId, request.UserId, request.Content);
|
var reply = new StoryReply(request.StoryId, request.UserId, request.Content);
|
||||||
_context.StoryReplies.Add(reply);
|
_context.StoryReplies.Add(reply);
|
||||||
@@ -89,7 +92,10 @@ internal sealed class AddStoryReplyCommandHandler : ICommandHandler<AddStoryRepl
|
|||||||
|
|
||||||
private string GetStoryQuote(Story story)
|
private string GetStoryQuote(Story story)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(story.Content)) return story.Content;
|
if (!string.IsNullOrEmpty(story.Content))
|
||||||
|
{
|
||||||
|
return story.Content;
|
||||||
|
}
|
||||||
return story.Type.ToLower() switch
|
return story.Type.ToLower() switch
|
||||||
{
|
{
|
||||||
"image" => "�� Фото",
|
"image" => "�� Фото",
|
||||||
@@ -105,7 +111,10 @@ internal sealed class AddStoryReplyCommandHandler : ICommandHandler<AddStoryRepl
|
|||||||
c.Type == ChatType.Personal &&
|
c.Type == ChatType.Personal &&
|
||||||
c.Members.Any(m => m.UserId == userId2));
|
c.Members.Any(m => m.UserId == userId2));
|
||||||
|
|
||||||
if (personalChat != null) return personalChat.Id;
|
if (personalChat != null)
|
||||||
|
{
|
||||||
|
return personalChat.Id;
|
||||||
|
}
|
||||||
|
|
||||||
var command = new Knot.Modules.Chats.Application.Chats.Create.CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { userId1, userId2 });
|
var command = new Knot.Modules.Chats.Application.Chats.Create.CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { userId1, userId2 });
|
||||||
var result = await _sender.Send(command, cancellationToken);
|
var result = await _sender.Send(command, cancellationToken);
|
||||||
|
|||||||
+9
-3
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -22,8 +22,14 @@ internal sealed class DeleteStoryCommandHandler : ICommandHandler<DeleteStoryCom
|
|||||||
public async Task<Result<MessageResponse>> Handle(DeleteStoryCommand request, CancellationToken cancellationToken)
|
public async Task<Result<MessageResponse>> Handle(DeleteStoryCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
var story = await _context.Stories.FindAsync(new object[] { request.StoryId }, cancellationToken);
|
||||||
if (story == null) return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
if (story.UserId != request.UserId) return Result.Failure<MessageResponse>(new Error("Unauthorized", "Unauthorized"));
|
{
|
||||||
|
return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
if (story.UserId != request.UserId)
|
||||||
|
{
|
||||||
|
return Result.Failure<MessageResponse>(new Error("Unauthorized", "Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
_context.Stories.Remove(story);
|
_context.Stories.Remove(story);
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
+5
-2
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -26,7 +26,10 @@ internal sealed class RemoveStoryReactionCommandHandler : ICommandHandler<Remove
|
|||||||
var reaction = await _context.StoryReactions
|
var reaction = await _context.StoryReactions
|
||||||
.FirstOrDefaultAsync(r => r.StoryId == request.StoryId && r.UserId == request.UserId && r.Emoji == request.Emoji, cancellationToken);
|
.FirstOrDefaultAsync(r => r.StoryId == request.StoryId && r.UserId == request.UserId && r.Emoji == request.Emoji, cancellationToken);
|
||||||
|
|
||||||
if (reaction == null) return Result.Success(new MessageResponse("Reaction not found"));
|
if (reaction == null)
|
||||||
|
{
|
||||||
|
return Result.Success(new MessageResponse("Reaction not found"));
|
||||||
|
}
|
||||||
|
|
||||||
_context.StoryReactions.Remove(reaction);
|
_context.StoryReactions.Remove(reaction);
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ internal sealed class ViewStoryCommandHandler : ICommandHandler<ViewStoryCommand
|
|||||||
.Include(s => s.Viewers)
|
.Include(s => s.Viewers)
|
||||||
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
||||||
|
|
||||||
if (story == null) return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<MessageResponse>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
|
||||||
if (story.UserId == request.UserId)
|
if (story.UserId == request.UserId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -55,12 +55,18 @@ internal sealed class GetStoriesQueryHandler : IQueryHandler<GetStoriesQuery, Li
|
|||||||
foreach (var uid in userIds)
|
foreach (var uid in userIds)
|
||||||
{
|
{
|
||||||
var u = await _userRepository.GetByIdAsync(uid, cancellationToken);
|
var u = await _userRepository.GetByIdAsync(uid, cancellationToken);
|
||||||
if (u != null) userMap[uid] = u;
|
if (u != null)
|
||||||
|
{
|
||||||
|
userMap[uid] = u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var group in groups)
|
foreach (var group in groups)
|
||||||
{
|
{
|
||||||
if (!userMap.TryGetValue(group.Key, out var user)) continue;
|
if (!userMap.TryGetValue(group.Key, out var user))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
result.Add(new StoryGroupDto(
|
result.Add(new StoryGroupDto(
|
||||||
new StoryUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
new StoryUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
||||||
@@ -83,7 +89,10 @@ internal sealed class GetStoriesQueryHandler : IQueryHandler<GetStoriesQuery, Li
|
|||||||
|
|
||||||
return Result.Success(result.OrderBy(r =>
|
return Result.Success(result.OrderBy(r =>
|
||||||
{
|
{
|
||||||
if (r.User.Id == request.UserId) return 0;
|
if (r.User.Id == request.UserId)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}).ToList());
|
}).ToList());
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -31,14 +31,23 @@ internal sealed class GetStoryRepliesQueryHandler : IQueryHandler<GetStoryReplie
|
|||||||
.Include(s => s.Replies)
|
.Include(s => s.Replies)
|
||||||
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
||||||
|
|
||||||
if (story == null) return Result.Failure<List<StoryReplyDto>>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
if (story.UserId != request.UserId) return Result.Failure<List<StoryReplyDto>>(new Error("Unauthorized", "Unauthorized"));
|
{
|
||||||
|
return Result.Failure<List<StoryReplyDto>>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
if (story.UserId != request.UserId)
|
||||||
|
{
|
||||||
|
return Result.Failure<List<StoryReplyDto>>(new Error("Unauthorized", "Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
var replies = new List<StoryReplyDto>();
|
var replies = new List<StoryReplyDto>();
|
||||||
foreach (var reply in story.Replies.OrderBy(r => r.CreatedAt))
|
foreach (var reply in story.Replies.OrderBy(r => r.CreatedAt))
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(reply.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(reply.UserId, cancellationToken);
|
||||||
if (user == null) continue;
|
if (user == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
replies.Add(new StoryReplyDto(
|
replies.Add(new StoryReplyDto(
|
||||||
reply.Id,
|
reply.Id,
|
||||||
|
|||||||
+12
-3
@@ -31,8 +31,14 @@ internal sealed class GetStoryViewersQueryHandler : IQueryHandler<GetStoryViewer
|
|||||||
.Include(s => s.Viewers)
|
.Include(s => s.Viewers)
|
||||||
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
.FirstOrDefaultAsync(s => s.Id == request.StoryId, cancellationToken);
|
||||||
|
|
||||||
if (story == null) return Result.Failure<List<StoryViewerDto>>(new Error("Story.NotFound", "Story not found"));
|
if (story == null)
|
||||||
if (story.UserId != request.UserId) return Result.Failure<List<StoryViewerDto>>(new Error("Unauthorized", "Unauthorized"));
|
{
|
||||||
|
return Result.Failure<List<StoryViewerDto>>(new Error("Story.NotFound", "Story not found"));
|
||||||
|
}
|
||||||
|
if (story.UserId != request.UserId)
|
||||||
|
{
|
||||||
|
return Result.Failure<List<StoryViewerDto>>(new Error("Unauthorized", "Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
var viewerIds = story.Viewers.Select(v => v.UserId).ToList();
|
var viewerIds = story.Viewers.Select(v => v.UserId).ToList();
|
||||||
var viewers = new List<StoryViewerDto>();
|
var viewers = new List<StoryViewerDto>();
|
||||||
@@ -40,7 +46,10 @@ internal sealed class GetStoryViewersQueryHandler : IQueryHandler<GetStoryViewer
|
|||||||
foreach (var viewerId in viewerIds)
|
foreach (var viewerId in viewerIds)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(viewerId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(viewerId, cancellationToken);
|
||||||
if (user == null) continue;
|
if (user == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var viewerRecord = story.Viewers.First(v => v.UserId == viewerId);
|
var viewerRecord = story.Viewers.First(v => v.UserId == viewerId);
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -37,7 +37,10 @@ internal sealed class GetUserStoriesQueryHandler : IQueryHandler<GetUserStoriesQ
|
|||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
var user = await _userRepository.GetByIdAsync(request.TargetUserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.TargetUserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<StoryGroupDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<StoryGroupDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var result = new StoryGroupDto(
|
var result = new StoryGroupDto(
|
||||||
new StoryUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
new StoryUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ public class AdminController : ControllerBase
|
|||||||
public async Task<IActionResult> GetUserDetails(Guid id, CancellationToken ct)
|
public async Task<IActionResult> GetUserDetails(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetUserDetailsQuery(id), ct);
|
var result = await _sender.Send(new GetUserDetailsQuery(id), ct);
|
||||||
if (result.IsFailure) return NotFound("User not found");
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound("User not found");
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ public sealed class AuthController : ControllerBase
|
|||||||
public async Task<IActionResult> GetMe([FromServices] IUserContext userContext, CancellationToken ct)
|
public async Task<IActionResult> GetMe([FromServices] IUserContext userContext, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetMeQuery(userContext.UserId), ct);
|
var result = await _sender.Send(new GetMeQuery(userContext.UserId), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(new { User = result.Value.User });
|
return Ok(new { User = result.Value.User });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetChats(CancellationToken ct)
|
public async Task<IActionResult> GetChats(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetChatsQuery(_userContext.UserId), ct);
|
var result = await _sender.Send(new GetChatsQuery(_userContext.UserId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +53,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
{
|
{
|
||||||
var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds);
|
var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds);
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -61,7 +67,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
{
|
{
|
||||||
var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { _userContext.UserId, request.UserId });
|
var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { _userContext.UserId, request.UserId });
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -79,7 +88,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
|
|
||||||
var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds);
|
var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds);
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -89,7 +101,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetOrCreateFavorites(CancellationToken ct)
|
public async Task<IActionResult> GetOrCreateFavorites(CancellationToken ct)
|
||||||
{
|
{
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -99,7 +114,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> UpdateChat(Guid id, [FromBody] UpdateChatRequest request, CancellationToken ct)
|
public async Task<IActionResult> UpdateChat(Guid id, [FromBody] UpdateChatRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -111,7 +129,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
var result = await _sender.Send(new LeaveOrDeleteChatCommand(id, _userContext.UserId), ct);
|
var result = await _sender.Send(new LeaveOrDeleteChatCommand(id, _userContext.UserId), ct);
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
if (result.Error.Code == "Unauthorized") return Forbid();
|
if (result.Error.Code == "Unauthorized")
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
@@ -121,7 +142,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> ClearChat(Guid id, CancellationToken ct)
|
public async Task<IActionResult> ClearChat(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new ClearChatCommand(id, _userContext.UserId), ct);
|
var result = await _sender.Send(new ClearChatCommand(id, _userContext.UserId), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +153,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> TogglePin(Guid id, CancellationToken ct)
|
public async Task<IActionResult> TogglePin(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new TogglePinCommand(id, _userContext.UserId), ct);
|
var result = await _sender.Send(new TogglePinCommand(id, _userContext.UserId), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +164,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> AddMembers(Guid id, [FromBody] AddMembersRequest request, CancellationToken ct)
|
public async Task<IActionResult> AddMembers(Guid id, [FromBody] AddMembersRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -147,7 +177,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> RemoveMember(Guid id, Guid userId, CancellationToken ct)
|
public async Task<IActionResult> RemoveMember(Guid id, Guid userId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -156,12 +189,18 @@ public sealed class ChatsController : ControllerBase
|
|||||||
[HttpPost("{id:guid}/avatar")]
|
[HttpPost("{id:guid}/avatar")]
|
||||||
public async Task<IActionResult> UploadGroupAvatar(Guid id, Microsoft.AspNetCore.Http.IFormFile avatar, CancellationToken ct)
|
public async Task<IActionResult> UploadGroupAvatar(Guid id, Microsoft.AspNetCore.Http.IFormFile avatar, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (avatar == null || avatar.Length == 0) return BadRequest("No file");
|
if (avatar == null || avatar.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file");
|
||||||
|
}
|
||||||
|
|
||||||
using var stream = avatar.OpenReadStream();
|
using var stream = avatar.OpenReadStream();
|
||||||
var result = await _sender.Send(new UploadGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct);
|
var result = await _sender.Send(new UploadGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct);
|
||||||
|
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -170,12 +209,18 @@ public sealed class ChatsController : ControllerBase
|
|||||||
[HttpPost("{id:guid}/avatar/crop")]
|
[HttpPost("{id:guid}/avatar/crop")]
|
||||||
public async Task<IActionResult> CropGroupAvatar(Guid id, [FromForm] Microsoft.AspNetCore.Http.IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
|
public async Task<IActionResult> CropGroupAvatar(Guid id, [FromForm] Microsoft.AspNetCore.Http.IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (avatar == null || avatar.Length == 0) return BadRequest("No file");
|
if (avatar == null || avatar.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file");
|
||||||
|
}
|
||||||
|
|
||||||
using var stream = avatar.OpenReadStream();
|
using var stream = avatar.OpenReadStream();
|
||||||
var result = await _sender.Send(new CropGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream, x, y, width, height), ct);
|
var result = await _sender.Send(new CropGroupAvatarCommand(id, _userContext.UserId, avatar.FileName, avatar.ContentType, stream, x, y, width, height), ct);
|
||||||
|
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
@@ -185,7 +230,10 @@ public sealed class ChatsController : ControllerBase
|
|||||||
public async Task<IActionResult> RemoveGroupAvatar(Guid id, CancellationToken ct)
|
public async Task<IActionResult> RemoveGroupAvatar(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
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(_userContext.UserId, result.Value), ct);
|
var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
|
||||||
return Ok(chatResult.Value);
|
return Ok(chatResult.Value);
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetFriends(CancellationToken ct)
|
public async Task<IActionResult> GetFriends(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetFriendsQuery(_userContext.UserId), ct);
|
var result = await _sender.Send(new GetFriendsQuery(_userContext.UserId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +38,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetRequests(CancellationToken ct)
|
public async Task<IActionResult> GetRequests(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetIncomingRequestsQuery(_userContext.UserId), ct);
|
var result = await _sender.Send(new GetIncomingRequestsQuery(_userContext.UserId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +49,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetOutgoingRequests(CancellationToken ct)
|
public async Task<IActionResult> GetOutgoingRequests(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetOutgoingRequestsQuery(_userContext.UserId), ct);
|
var result = await _sender.Send(new GetOutgoingRequestsQuery(_userContext.UserId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +60,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> SendRequest([FromBody] Host.Models.SendFriendRequest request, CancellationToken ct)
|
public async Task<IActionResult> SendRequest([FromBody] Host.Models.SendFriendRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new SendFriendRequestCommand(_userContext.UserId, request.FriendId), ct);
|
var result = await _sender.Send(new SendFriendRequestCommand(_userContext.UserId, request.FriendId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(new { status = "pending" });
|
return Ok(new { status = "pending" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +71,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> AcceptRequest(Guid id, CancellationToken ct)
|
public async Task<IActionResult> AcceptRequest(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new AcceptFriendRequestCommand(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new AcceptFriendRequestCommand(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(new { id = result.Value });
|
return Ok(new { id = result.Value });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +82,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> DeclineRequest(Guid id, CancellationToken ct)
|
public async Task<IActionResult> DeclineRequest(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new DeclineFriendRequestCommand(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new DeclineFriendRequestCommand(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
|
return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +93,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> GetStatus(Guid userId, CancellationToken ct)
|
public async Task<IActionResult> GetStatus(Guid userId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetFriendshipStatusQuery(_userContext.UserId, userId), ct);
|
var result = await _sender.Send(new GetFriendshipStatusQuery(_userContext.UserId, userId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +104,10 @@ public sealed class FriendsController : ControllerBase
|
|||||||
public async Task<IActionResult> RemoveFriend(Guid id, CancellationToken ct)
|
public async Task<IActionResult> RemoveFriend(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new RemoveFriendCommand(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new RemoveFriendCommand(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
|
return Ok(new Knot.Shared.Kernel.SuccessResponse(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,10 @@ public sealed class MessagesController : ControllerBase
|
|||||||
public async Task<IActionResult> GetMessages(Guid chatId, [FromQuery] string? cursor, CancellationToken ct = default)
|
public async Task<IActionResult> GetMessages(Guid chatId, [FromQuery] string? cursor, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetMessagesQuery(_userContext.UserId, chatId, cursor), ct);
|
var result = await _sender.Send(new GetMessagesQuery(_userContext.UserId, chatId, cursor), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,21 +45,30 @@ public sealed class MessagesController : ControllerBase
|
|||||||
public async Task<IActionResult> GetSearch([FromQuery] string q, [FromQuery] Guid? chatId, CancellationToken ct)
|
public async Task<IActionResult> GetSearch([FromQuery] string q, [FromQuery] Guid? chatId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new SearchMessagesQuery(_userContext.UserId, q, chatId), ct);
|
var result = await _sender.Send(new SearchMessagesQuery(_userContext.UserId, q, chatId), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("upload")]
|
[HttpPost("upload")]
|
||||||
public async Task<IActionResult> UploadFile(IFormFile file, CancellationToken ct)
|
public async Task<IActionResult> UploadFile(IFormFile file, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (file == null || file.Length == 0) return BadRequest("No file uploaded");
|
if (file == null || file.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
using var stream = file.OpenReadStream();
|
using var stream = file.OpenReadStream();
|
||||||
var result = await _sender.Send(new UploadFileCommand(file.FileName, file.ContentType, file.Length, stream), ct);
|
var result = await _sender.Send(new UploadFileCommand(file.FileName, file.ContentType, file.Length, stream), ct);
|
||||||
|
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
if (result.Error.Code == "File.TooLarge") return StatusCode(413, result.Error.Description);
|
if (result.Error.Code == "File.TooLarge")
|
||||||
|
{
|
||||||
|
return StatusCode(413, result.Error.Description);
|
||||||
|
}
|
||||||
return BadRequest(result.Error.Description);
|
return BadRequest(result.Error.Description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +79,10 @@ public sealed class MessagesController : ControllerBase
|
|||||||
public async Task<IActionResult> GetSharedMedia(Guid chatId, [FromQuery] string? type, CancellationToken ct)
|
public async Task<IActionResult> GetSharedMedia(Guid chatId, [FromQuery] string? type, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetSharedMediaQuery(_userContext.UserId, chatId, type), ct);
|
var result = await _sender.Send(new GetSharedMediaQuery(_userContext.UserId, chatId, type), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error.Description);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error.Description);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +104,10 @@ public sealed class MessagesController : 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);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
public async Task<IActionResult> GetUserStories(Guid userId, CancellationToken ct)
|
public async Task<IActionResult> GetUserStories(Guid userId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetUserStoriesQuery(_userContext.UserId, userId), ct);
|
var result = await _sender.Send(new GetUserStoriesQuery(_userContext.UserId, userId), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +62,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
public async Task<IActionResult> ViewStory(Guid id, CancellationToken ct)
|
public async Task<IActionResult> ViewStory(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new ViewStoryCommand(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new ViewStoryCommand(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +75,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
var result = await _sender.Send(new GetStoryViewersQuery(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new GetStoryViewersQuery(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
if (result.Error.Code == "Unauthorized") return Forbid();
|
if (result.Error.Code == "Unauthorized")
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
@@ -79,7 +88,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
public async Task<IActionResult> AddReaction(Guid id, [FromBody] AddStoryReactionRequest request, CancellationToken ct)
|
public async Task<IActionResult> AddReaction(Guid id, [FromBody] AddStoryReactionRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new AddStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
|
var result = await _sender.Send(new AddStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +99,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
public async Task<IActionResult> RemoveReaction(Guid id, [FromBody] RemoveStoryReactionRequest request, CancellationToken ct)
|
public async Task<IActionResult> RemoveReaction(Guid id, [FromBody] RemoveStoryReactionRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new RemoveStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
|
var result = await _sender.Send(new RemoveStoryReactionCommand(_userContext.UserId, id, request.Emoji), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +110,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
public async Task<IActionResult> AddReply(Guid id, [FromBody] AddStoryReplyRequest request, CancellationToken ct)
|
public async Task<IActionResult> AddReply(Guid id, [FromBody] AddStoryReplyRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new AddStoryReplyCommand(_userContext.UserId, id, request.Content), ct);
|
var result = await _sender.Send(new AddStoryReplyCommand(_userContext.UserId, id, request.Content), ct);
|
||||||
if (result.IsFailure) return NotFound();
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +123,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
var result = await _sender.Send(new GetStoryRepliesQuery(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new GetStoryRepliesQuery(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
if (result.Error.Code == "Unauthorized") return Forbid();
|
if (result.Error.Code == "Unauthorized")
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
@@ -117,7 +138,10 @@ public sealed class StoriesController : ControllerBase
|
|||||||
var result = await _sender.Send(new DeleteStoryCommand(_userContext.UserId, id), ct);
|
var result = await _sender.Send(new DeleteStoryCommand(_userContext.UserId, id), ct);
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
if (result.Error.Code == "Unauthorized") return Forbid();
|
if (result.Error.Code == "Unauthorized")
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> Search([FromQuery] string q, CancellationToken ct)
|
public async Task<IActionResult> Search([FromQuery] string q, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new SearchUsersQuery(q), ct);
|
var result = await _sender.Send(new SearchUsersQuery(q), ct);
|
||||||
if (result.IsFailure) return BadRequest(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return BadRequest(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +42,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> UpdateSettings([FromBody] UpdateSettingsRequest request, CancellationToken ct)
|
public async Task<IActionResult> UpdateSettings([FromBody] UpdateSettingsRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new UpdateSettingsCommand(_userContext.UserId, request.HideStoryViews), ct);
|
var result = await _sender.Send(new UpdateSettingsCommand(_userContext.UserId, request.HideStoryViews), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +53,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> UploadAvatar(IFormFile avatar, CancellationToken ct)
|
public async Task<IActionResult> UploadAvatar(IFormFile avatar, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var fileToUpload = avatar ?? Request.Form.Files.FirstOrDefault();
|
var fileToUpload = avatar ?? Request.Form.Files.FirstOrDefault();
|
||||||
if (fileToUpload == null || fileToUpload.Length == 0) return BadRequest("No file uploaded");
|
if (fileToUpload == null || fileToUpload.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
using var stream = fileToUpload.OpenReadStream();
|
using var stream = fileToUpload.OpenReadStream();
|
||||||
var command = new UploadAvatarCommand(
|
var command = new UploadAvatarCommand(
|
||||||
@@ -58,14 +67,20 @@ public sealed class UsersController : ControllerBase
|
|||||||
);
|
);
|
||||||
|
|
||||||
var result = await _sender.Send(command, ct);
|
var result = await _sender.Send(command, ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("avatar/crop")]
|
[HttpPost("avatar/crop")]
|
||||||
public async Task<IActionResult> CropAvatar([FromForm] IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
|
public async Task<IActionResult> CropAvatar([FromForm] IFormFile avatar, [FromForm] int x, [FromForm] int y, [FromForm] int width, [FromForm] int height, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (avatar == null || avatar.Length == 0) return BadRequest("No file uploaded");
|
if (avatar == null || avatar.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
using var stream = avatar.OpenReadStream();
|
using var stream = avatar.OpenReadStream();
|
||||||
var command = new CropAvatarCommand(
|
var command = new CropAvatarCommand(
|
||||||
@@ -77,7 +92,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
);
|
);
|
||||||
|
|
||||||
var result = await _sender.Send(command, ct);
|
var result = await _sender.Send(command, ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +103,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> DeleteAvatar(CancellationToken ct)
|
public async Task<IActionResult> DeleteAvatar(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new DeleteAvatarCommand(_userContext.UserId), ct);
|
var result = await _sender.Send(new DeleteAvatarCommand(_userContext.UserId), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +114,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> UpdateProfile([FromBody] UpdateProfileRequest request, CancellationToken ct)
|
public async Task<IActionResult> UpdateProfile([FromBody] UpdateProfileRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new UpdateProfileCommand(_userContext.UserId, request.DisplayName, request.Bio, request.Birthday), ct);
|
var result = await _sender.Send(new UpdateProfileCommand(_userContext.UserId, request.DisplayName, request.Bio, request.Birthday), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +125,10 @@ public sealed class UsersController : ControllerBase
|
|||||||
public async Task<IActionResult> GetUser(Guid id, CancellationToken ct)
|
public async Task<IActionResult> GetUser(Guid id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var result = await _sender.Send(new GetUserQuery(id), ct);
|
var result = await _sender.Send(new GetUserQuery(id), ct);
|
||||||
if (result.IsFailure) return NotFound(result.Error);
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NotFound(result.Error);
|
||||||
|
}
|
||||||
return Ok(result.Value);
|
return Ok(result.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,19 @@ public sealed class FederationEndpoints : ICarterModule
|
|||||||
group.MapPost("/handshake", async ([FromBody] Host.Application.Federation.Commands.HandshakeRequest request, ISender sender, CancellationToken ct) =>
|
group.MapPost("/handshake", async ([FromBody] Host.Application.Federation.Commands.HandshakeRequest request, ISender sender, CancellationToken ct) =>
|
||||||
{
|
{
|
||||||
var result = await sender.Send(new Host.Application.Federation.Commands.HandshakeFederationCommand(request), ct);
|
var result = await sender.Send(new Host.Application.Federation.Commands.HandshakeFederationCommand(request), ct);
|
||||||
if (result.IsSuccess) return Results.Ok(result.Value);
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
return Results.Ok(result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.Error.Code == "Unauthorized") return Results.Forbid();
|
if (result.Error.Code == "Unauthorized")
|
||||||
if (result.Error.Code == Knot.Shared.Kernel.Constants.Errors.DisabledByAdmin) return Results.StatusCode(503);
|
{
|
||||||
|
return Results.Forbid();
|
||||||
|
}
|
||||||
|
if (result.Error.Code == Knot.Shared.Kernel.Constants.Errors.DisabledByAdmin)
|
||||||
|
{
|
||||||
|
return Results.StatusCode(503);
|
||||||
|
}
|
||||||
|
|
||||||
return Results.BadRequest(new { error = result.Error.Description });
|
return Results.BadRequest(new { error = result.Error.Description });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ internal sealed class UploadGroupAvatarCommandHandler : ICommandHandler<UploadGr
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
||||||
var url = $"/api/files/{fileId}";
|
var url = $"/api/files/{fileId}";
|
||||||
@@ -64,7 +66,9 @@ internal sealed class CropGroupAvatarCommandHandler : ICommandHandler<CropGroupA
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
string url;
|
string url;
|
||||||
|
|
||||||
@@ -112,7 +116,9 @@ internal sealed class RemoveGroupAvatarCommandHandler : ICommandHandler<RemoveGr
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
chat.UpdateAvatar(null);
|
chat.UpdateAvatar(null);
|
||||||
_chatRepository.Update(chat);
|
_chatRepository.Update(chat);
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ internal sealed class ClearChatCommandHandler : ICommandHandler<ClearChatCommand
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<MessageResponse>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<MessageResponse>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
// Currently a placeholder
|
// Currently a placeholder
|
||||||
return Result.Success(new MessageResponse("Cleared"));
|
return Result.Success(new MessageResponse("Cleared"));
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery,
|
|||||||
public async Task<Result<ChatDto?>> Handle(GetChatByIdQuery request, CancellationToken cancellationToken)
|
public async Task<Result<ChatDto?>> Handle(GetChatByIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
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))
|
if (!chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
{
|
{
|
||||||
@@ -37,14 +40,20 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
var chatMessages = await _messageRepository.GetChatMessagesAsync(chat.Id, 1, 0, cancellationToken);
|
var chatMessages = await _messageRepository.GetChatMessagesAsync(chat.Id, 1, 0, cancellationToken);
|
||||||
var mFirst = chatMessages.FirstOrDefault();
|
var mFirst = chatMessages.FirstOrDefault();
|
||||||
if (mFirst != null)
|
if (mFirst != null)
|
||||||
{
|
{
|
||||||
userIdsToFetch.Add(mFirst.SenderId);
|
userIdsToFetch.Add(mFirst.SenderId);
|
||||||
foreach (var r in mFirst.Reactions) userIdsToFetch.Add(r.UserId);
|
foreach (var r in mFirst.Reactions)
|
||||||
|
{
|
||||||
|
userIdsToFetch.Add(r.UserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken);
|
var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken);
|
||||||
|
|||||||
@@ -36,14 +36,20 @@ internal sealed class GetChatsQueryHandler : IQueryHandler<GetChatsQuery, List<C
|
|||||||
foreach (var c in userChats)
|
foreach (var c in userChats)
|
||||||
{
|
{
|
||||||
var userIdsToFetch = new HashSet<Guid>();
|
var userIdsToFetch = new HashSet<Guid>();
|
||||||
foreach (var m in c.Members) userIdsToFetch.Add(m.UserId);
|
foreach (var m in c.Members)
|
||||||
|
{
|
||||||
|
userIdsToFetch.Add(m.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
var chatMessages = await _messageRepository.GetChatMessagesAsync(c.Id, 1, 0, cancellationToken);
|
var chatMessages = await _messageRepository.GetChatMessagesAsync(c.Id, 1, 0, cancellationToken);
|
||||||
var mFirst = chatMessages.FirstOrDefault();
|
var mFirst = chatMessages.FirstOrDefault();
|
||||||
if (mFirst != null)
|
if (mFirst != null)
|
||||||
{
|
{
|
||||||
userIdsToFetch.Add(mFirst.SenderId);
|
userIdsToFetch.Add(mFirst.SenderId);
|
||||||
foreach (var r in mFirst.Reactions) userIdsToFetch.Add(r.UserId);
|
foreach (var r in mFirst.Reactions)
|
||||||
|
{
|
||||||
|
userIdsToFetch.Add(r.UserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken);
|
var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken);
|
||||||
|
|||||||
+6
-1
@@ -25,10 +25,15 @@ internal sealed class LeaveOrDeleteChatCommandHandler : ICommandHandler<LeaveOrD
|
|||||||
public async Task<Result<SuccessResponse>> Handle(LeaveOrDeleteChatCommand request, CancellationToken cancellationToken)
|
public async Task<Result<SuccessResponse>> Handle(LeaveOrDeleteChatCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null) return Result.Success(new SuccessResponse(true));
|
if (chat == null)
|
||||||
|
{
|
||||||
|
return Result.Success(new SuccessResponse(true));
|
||||||
|
}
|
||||||
|
|
||||||
if (!chat.Members.Any(m => m.UserId == request.UserId))
|
if (!chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<SuccessResponse>(new Error("Unauthorized", "Access denied"));
|
return Result.Failure<SuccessResponse>(new Error("Unauthorized", "Access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
if (chat.Type == ChatType.Group)
|
if (chat.Type == ChatType.Group)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ internal sealed class AddMembersCommandHandler : ICommandHandler<AddMembersComma
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var userId in request.UserIdsToAdd)
|
foreach (var userId in request.UserIdsToAdd)
|
||||||
{
|
{
|
||||||
@@ -57,7 +59,9 @@ internal sealed class RemoveMemberCommandHandler : ICommandHandler<RemoveMemberC
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
chat.RemoveMember(request.UserIdToRemove);
|
chat.RemoveMember(request.UserIdToRemove);
|
||||||
_chatRepository.Update(chat);
|
_chatRepository.Update(chat);
|
||||||
|
|||||||
@@ -27,11 +27,15 @@ internal sealed class TogglePinCommandHandler : ICommandHandler<TogglePinCommand
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null)
|
if (chat == null)
|
||||||
|
{
|
||||||
return Result.Failure<TogglePinResponse>(new Error("Chat.NotFound", "Chat not found"));
|
return Result.Failure<TogglePinResponse>(new Error("Chat.NotFound", "Chat not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var member = chat.Members.FirstOrDefault(m => m.UserId == request.UserId);
|
var member = chat.Members.FirstOrDefault(m => m.UserId == request.UserId);
|
||||||
if (member == null)
|
if (member == null)
|
||||||
|
{
|
||||||
return Result.Failure<TogglePinResponse>(new Error("Chat.NotFound", "Member not found"));
|
return Result.Failure<TogglePinResponse>(new Error("Chat.NotFound", "Member not found"));
|
||||||
|
}
|
||||||
|
|
||||||
member.TogglePin();
|
member.TogglePin();
|
||||||
_chatRepository.Update(chat);
|
_chatRepository.Update(chat);
|
||||||
|
|||||||
@@ -26,10 +26,19 @@ internal sealed class UpdateChatCommandHandler : ICommandHandler<UpdateChatComma
|
|||||||
{
|
{
|
||||||
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
||||||
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
return Result.Failure<Guid>(new Error("Chat.NotFound", "Chat not found or access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
if (request.Name != null) chat.UpdateName(request.Name);
|
if (request.Name != null)
|
||||||
if (request.Description != null) chat.UpdateDescription(request.Description);
|
{
|
||||||
|
chat.UpdateName(request.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Description != null)
|
||||||
|
{
|
||||||
|
chat.UpdateDescription(request.Description);
|
||||||
|
}
|
||||||
|
|
||||||
_chatRepository.Update(chat);
|
_chatRepository.Update(chat);
|
||||||
await _uow.SaveChangesAsync(cancellationToken);
|
await _uow.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
+4
-1
@@ -34,7 +34,10 @@ public sealed class DeleteMessagesCommandHandler : ICommandHandler<DeleteMessage
|
|||||||
foreach (var id in request.MessageIds)
|
foreach (var id in request.MessageIds)
|
||||||
{
|
{
|
||||||
var message = await _messageRepository.GetByIdAsync(id, cancellationToken);
|
var message = await _messageRepository.GetByIdAsync(id, cancellationToken);
|
||||||
if (message is null || message.ChatId != request.ChatId) continue;
|
if (message is null || message.ChatId != request.ChatId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (request.DeleteForAll)
|
if (request.DeleteForAll)
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-3
@@ -49,13 +49,21 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler<GetMessagesQuery,
|
|||||||
|
|
||||||
foreach (var m in messages)
|
foreach (var m in messages)
|
||||||
{
|
{
|
||||||
if (m.DeletedByUsers.Contains(request.UserId)) continue;
|
if (m.DeletedByUsers.Contains(request.UserId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
userIdsToFetch.Add(m.SenderId);
|
userIdsToFetch.Add(m.SenderId);
|
||||||
if (m.ForwardedFromId.HasValue) userIdsToFetch.Add(m.ForwardedFromId.Value);
|
if (m.ForwardedFromId.HasValue)
|
||||||
|
{
|
||||||
|
userIdsToFetch.Add(m.ForwardedFromId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var r in m.Reactions)
|
foreach (var r in m.Reactions)
|
||||||
|
{
|
||||||
userIdsToFetch.Add(r.UserId);
|
userIdsToFetch.Add(r.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
if (m.ReplyToId.HasValue)
|
if (m.ReplyToId.HasValue)
|
||||||
{
|
{
|
||||||
@@ -72,7 +80,10 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler<GetMessagesQuery,
|
|||||||
|
|
||||||
foreach (var m in messages)
|
foreach (var m in messages)
|
||||||
{
|
{
|
||||||
if (m.DeletedByUsers.Contains(request.UserId)) continue;
|
if (m.DeletedByUsers.Contains(request.UserId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ReplyToMessageDto? replyToObj = null;
|
ReplyToMessageDto? replyToObj = null;
|
||||||
if (m.ReplyToId.HasValue && replyMessages.TryGetValue(m.ReplyToId.Value, out var replyMsg))
|
if (m.ReplyToId.HasValue && replyMessages.TryGetValue(m.ReplyToId.Value, out var replyMsg))
|
||||||
|
|||||||
+21
-5
@@ -69,15 +69,31 @@ internal sealed class GetSharedMediaQueryHandler : IQueryHandler<GetSharedMediaQ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.Media == null || !m.Media.Any()) continue;
|
if (m.Media == null || !m.Media.Any())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var filteredMedia = m.Media.Where(media => {
|
var filteredMedia = m.Media.Where(media =>
|
||||||
|
{
|
||||||
var mediaType = media.Type?.ToLower() ?? "file";
|
var mediaType = media.Type?.ToLower() ?? "file";
|
||||||
var isGif = mediaType == "image" && (media.Url.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase) || media.Url.EndsWith(".gif", StringComparison.OrdinalIgnoreCase));
|
var isGif = mediaType == "image" && (media.Url.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase) || media.Url.EndsWith(".gif", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (filterType == "media") return (mediaType == "image" || mediaType == "video") && !isGif;
|
if (filterType == "media")
|
||||||
if (filterType == "gifs") return isGif;
|
{
|
||||||
if (filterType == "files") return mediaType != "image" && mediaType != "video" && mediaType != "link";
|
return (mediaType == "image" || mediaType == "video") && !isGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterType == "gifs")
|
||||||
|
{
|
||||||
|
return isGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterType == "files")
|
||||||
|
{
|
||||||
|
return mediaType != "image" && mediaType != "video" && mediaType != "link";
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -25,11 +25,16 @@ internal sealed class UploadFileCommandHandler : ICommandHandler<UploadFileComma
|
|||||||
|
|
||||||
public async Task<Result<UploadFileResponseDto>> Handle(UploadFileCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UploadFileResponseDto>> Handle(UploadFileCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (request.Length == 0) return Result.Failure<UploadFileResponseDto>(new Error("File.Empty", "No file uploaded"));
|
if (request.Length == 0)
|
||||||
|
{
|
||||||
|
return Result.Failure<UploadFileResponseDto>(new Error("File.Empty", "No file uploaded"));
|
||||||
|
}
|
||||||
|
|
||||||
var maxMb = _settingsService.Current.MaxFileSizeMb;
|
var maxMb = _settingsService.Current.MaxFileSizeMb;
|
||||||
if (request.Length > maxMb * 1024 * 1024)
|
if (request.Length > maxMb * 1024 * 1024)
|
||||||
|
{
|
||||||
return Result.Failure<UploadFileResponseDto>(new Error("File.TooLarge", $"File exceeds the maximum allowed size of {maxMb}MB."));
|
return Result.Failure<UploadFileResponseDto>(new Error("File.TooLarge", $"File exceeds the maximum allowed size of {maxMb}MB."));
|
||||||
|
}
|
||||||
|
|
||||||
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -27,10 +27,14 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler<AnalyzeImpor
|
|||||||
public async Task<Result<AnalyzeImportResponseDto>> Handle(AnalyzeImportCommand request, CancellationToken cancellationToken)
|
public async Task<Result<AnalyzeImportResponseDto>> Handle(AnalyzeImportCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (request.FileStream == null || request.FileStream.Length == 0)
|
if (request.FileStream == null || request.FileStream.Length == 0)
|
||||||
|
{
|
||||||
return Result.Failure<AnalyzeImportResponseDto>(new Error("File.Empty", "No file uploaded"));
|
return Result.Failure<AnalyzeImportResponseDto>(new Error("File.Empty", "No file uploaded"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
return Result.Failure<AnalyzeImportResponseDto>(new Error("File.InvalidExtension", "Must be a ZIP archive"));
|
return Result.Failure<AnalyzeImportResponseDto>(new Error("File.InvalidExtension", "Must be a ZIP archive"));
|
||||||
|
}
|
||||||
|
|
||||||
var token = Guid.NewGuid();
|
var token = Guid.NewGuid();
|
||||||
var tempPath = Path.Combine(Path.GetTempPath(), $"{token}.zip");
|
var tempPath = Path.Combine(Path.GetTempPath(), $"{token}.zip");
|
||||||
@@ -54,7 +58,10 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler<AnalyzeImpor
|
|||||||
var doc = parser.ParseDocument(stream);
|
var doc = parser.ParseDocument(stream);
|
||||||
|
|
||||||
var messageNodes = doc.QuerySelectorAll(".message");
|
var messageNodes = doc.QuerySelectorAll(".message");
|
||||||
if (messageNodes == null) continue;
|
if (messageNodes == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var node in messageNodes)
|
foreach (var node in messageNodes)
|
||||||
{
|
{
|
||||||
@@ -63,7 +70,10 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler<AnalyzeImpor
|
|||||||
{
|
{
|
||||||
var nameNodeText = (IElement)fromNameNode.Clone();
|
var nameNodeText = (IElement)fromNameNode.Clone();
|
||||||
var innerSpans = nameNodeText.QuerySelectorAll("span");
|
var innerSpans = nameNodeText.QuerySelectorAll("span");
|
||||||
foreach (var span in innerSpans) span.Remove();
|
foreach (var span in innerSpans)
|
||||||
|
{
|
||||||
|
span.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
var name = nameNodeText.TextContent.Trim();
|
var name = nameNodeText.TextContent.Trim();
|
||||||
if (!string.IsNullOrWhiteSpace(name))
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
|
|||||||
+55
-10
@@ -55,14 +55,21 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
public async Task<Result<ExecuteImportResponseDto>> Handle(ExecuteImportCommand request, CancellationToken cancellationToken)
|
public async Task<Result<ExecuteImportResponseDto>> Handle(ExecuteImportCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!TelegramImportState.TempZips.TryGetValue(request.Token, out var tempPath))
|
if (!TelegramImportState.TempZips.TryGetValue(request.Token, out var tempPath))
|
||||||
|
{
|
||||||
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.Expired", "Session not found or expired"));
|
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.Expired", "Session not found or expired"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!System.IO.File.Exists(tempPath))
|
if (!System.IO.File.Exists(tempPath))
|
||||||
|
{
|
||||||
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.Missing", "ZIP file lost"));
|
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.Missing", "ZIP file lost"));
|
||||||
|
}
|
||||||
|
|
||||||
var myId = request.CurrentUserId;
|
var myId = request.CurrentUserId;
|
||||||
var targetUserIds = request.Mapping.Values.Distinct().Where(id => id != Guid.Empty).ToList();
|
var targetUserIds = request.Mapping.Values.Distinct().Where(id => id != Guid.Empty).ToList();
|
||||||
if (!targetUserIds.Contains(myId)) targetUserIds.Add(myId);
|
if (!targetUserIds.Contains(myId))
|
||||||
|
{
|
||||||
|
targetUserIds.Add(myId);
|
||||||
|
}
|
||||||
|
|
||||||
Guid chatId = Guid.Empty;
|
Guid chatId = Guid.Empty;
|
||||||
var chatMembers = targetUserIds;
|
var chatMembers = targetUserIds;
|
||||||
@@ -79,10 +86,18 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var friendId = chatMembers.FirstOrDefault(id => id != myId);
|
var friendId = chatMembers.FirstOrDefault(id => id != myId);
|
||||||
if (friendId == Guid.Empty) friendId = myId;
|
if (friendId == Guid.Empty)
|
||||||
|
{
|
||||||
|
friendId = myId;
|
||||||
|
}
|
||||||
|
|
||||||
var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { myId, friendId });
|
var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { myId, friendId });
|
||||||
var res = await _sender.Send(command, cancellationToken);
|
var res = await _sender.Send(command, cancellationToken);
|
||||||
if (res.IsFailure) return Result.Failure<ExecuteImportResponseDto>(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code));
|
if (res.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code));
|
||||||
|
}
|
||||||
|
|
||||||
chatId = res.Value;
|
chatId = res.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +105,11 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
{
|
{
|
||||||
var command = new CreateChatCommand(request.GroupName ?? "Импортированный чат", ChatType.Group, chatMembers);
|
var command = new CreateChatCommand(request.GroupName ?? "Импортированный чат", ChatType.Group, chatMembers);
|
||||||
var res = await _sender.Send(command, cancellationToken);
|
var res = await _sender.Send(command, cancellationToken);
|
||||||
if (res.IsFailure) return Result.Failure<ExecuteImportResponseDto>(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code));
|
if (res.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure<ExecuteImportResponseDto>(new Error("Import.CreateChatFailed", res.Error.Description ?? res.Error.Code));
|
||||||
|
}
|
||||||
|
|
||||||
chatId = res.Value;
|
chatId = res.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +138,16 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
var doc = parser.ParseDocument(stream);
|
var doc = parser.ParseDocument(stream);
|
||||||
|
|
||||||
var messageNodes = doc.QuerySelectorAll(".message");
|
var messageNodes = doc.QuerySelectorAll(".message");
|
||||||
if (messageNodes == null) continue;
|
if (messageNodes == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var baseDir = Path.GetDirectoryName(entry.FullName)?.Replace("\\", "/") ?? "";
|
var baseDir = Path.GetDirectoryName(entry.FullName)?.Replace("\\", "/") ?? "";
|
||||||
if (!string.IsNullOrEmpty(baseDir) && !baseDir.EndsWith("/")) baseDir += "/";
|
if (!string.IsNullOrEmpty(baseDir) && !baseDir.EndsWith("/"))
|
||||||
|
{
|
||||||
|
baseDir += "/";
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var node in messageNodes)
|
foreach (var node in messageNodes)
|
||||||
{
|
{
|
||||||
@@ -139,14 +164,21 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
{
|
{
|
||||||
var nameNodeText = (AngleSharp.Dom.IElement)fromNameNode.Clone();
|
var nameNodeText = (AngleSharp.Dom.IElement)fromNameNode.Clone();
|
||||||
var innerSpans = nameNodeText.QuerySelectorAll("span");
|
var innerSpans = nameNodeText.QuerySelectorAll("span");
|
||||||
foreach (var span in innerSpans) span.Remove();
|
foreach (var span in innerSpans)
|
||||||
|
{
|
||||||
|
span.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
var name = nameNodeText.TextContent.Trim();
|
var name = nameNodeText.TextContent.Trim();
|
||||||
if (request.Mapping.TryGetValue(name, out var mappedId) && mappedId != Guid.Empty)
|
if (request.Mapping.TryGetValue(name, out var mappedId) && mappedId != Guid.Empty)
|
||||||
|
{
|
||||||
lastSenderGuid = mappedId;
|
lastSenderGuid = mappedId;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
lastSenderGuid = myId;
|
lastSenderGuid = myId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Guid senderGuid = lastSenderGuid;
|
Guid senderGuid = lastSenderGuid;
|
||||||
|
|
||||||
@@ -223,7 +255,10 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
if (fwdNameText != null)
|
if (fwdNameText != null)
|
||||||
{
|
{
|
||||||
var innerSpans = fwdNameText.QuerySelectorAll("span");
|
var innerSpans = fwdNameText.QuerySelectorAll("span");
|
||||||
foreach (var s in innerSpans) s.Remove();
|
foreach (var s in innerSpans)
|
||||||
|
{
|
||||||
|
s.Remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var fwdName = fwdNameText != null ? fwdNameText.TextContent.Trim() : "Неизвестного";
|
var fwdName = fwdNameText != null ? fwdNameText.TextContent.Trim() : "Неизвестного";
|
||||||
|
|
||||||
@@ -306,10 +341,12 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
if (mediaNodes[0].ClassName?.Contains("animated") == true || firstHref.EndsWith(".mp4"))
|
if (mediaNodes[0].ClassName?.Contains("animated") == true || firstHref.EndsWith(".mp4"))
|
||||||
{
|
{
|
||||||
if (mediaNodes[0].ClassName?.Contains("animated") == true)
|
if (mediaNodes[0].ClassName?.Contains("animated") == true)
|
||||||
|
{
|
||||||
messageType = "image";
|
messageType = "image";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isJoined = fromNameNode == null;
|
bool isJoined = fromNameNode == null;
|
||||||
bool isMediaOnly = string.IsNullOrEmpty(content) && forwardedNode == null && replyToId == null;
|
bool isMediaOnly = string.IsNullOrEmpty(content) && forwardedNode == null && replyToId == null;
|
||||||
@@ -349,7 +386,11 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
|
|
||||||
var types = GetMediaTypes(href);
|
var types = GetMediaTypes(href);
|
||||||
var finalMType = types.mType;
|
var finalMType = types.mType;
|
||||||
if (mediaNode.ClassName?.Contains("animated") == true) finalMType = "image";
|
if (mediaNode.ClassName?.Contains("animated") == true)
|
||||||
|
{
|
||||||
|
finalMType = "image";
|
||||||
|
}
|
||||||
|
|
||||||
var fileId = await _fileStorage.UploadFileAsync(ms, Path.GetFileName(href), types.cType);
|
var fileId = await _fileStorage.UploadFileAsync(ms, Path.GetFileName(href), types.cType);
|
||||||
targetMessage.AddMedia(finalMType, $"/api/files/{fileId}", Path.GetFileName(href), zipEntry.Length);
|
targetMessage.AddMedia(finalMType, $"/api/files/{fileId}", Path.GetFileName(href), zipEntry.Length);
|
||||||
}
|
}
|
||||||
@@ -361,7 +402,11 @@ internal sealed class ExecuteImportCommandHandler : ICommandHandler<ExecuteImpor
|
|||||||
foreach (var reactionNode in reactionNodes)
|
foreach (var reactionNode in reactionNodes)
|
||||||
{
|
{
|
||||||
var emojiNode = reactionNode.QuerySelector(".emoji");
|
var emojiNode = reactionNode.QuerySelector(".emoji");
|
||||||
if (emojiNode == null) continue;
|
if (emojiNode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var emoji = emojiNode.TextContent.Trim();
|
var emoji = emojiNode.TextContent.Trim();
|
||||||
|
|
||||||
var userpicNodes = reactionNode.QuerySelectorAll(".userpics .userpic .initials[title]");
|
var userpicNodes = reactionNode.QuerySelectorAll(".userpics .userpic .initials[title]");
|
||||||
|
|||||||
@@ -80,7 +80,11 @@ public sealed class Chat : AggregateRoot<Guid>
|
|||||||
|
|
||||||
public void AddMember(Guid userId, string role = "member")
|
public void AddMember(Guid userId, string role = "member")
|
||||||
{
|
{
|
||||||
if (_members.Any(m => m.UserId == userId)) return;
|
if (_members.Any(m => m.UserId == userId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_members.Add(new ChatMember(Id, userId, role));
|
_members.Add(new ChatMember(Id, userId, role));
|
||||||
RaiseDomainEvent(new ChatMemberAddedDomainEvent(Id, userId));
|
RaiseDomainEvent(new ChatMemberAddedDomainEvent(Id, userId));
|
||||||
}
|
}
|
||||||
@@ -88,7 +92,10 @@ public sealed class Chat : AggregateRoot<Guid>
|
|||||||
public void RemoveMember(Guid userId)
|
public void RemoveMember(Guid userId)
|
||||||
{
|
{
|
||||||
var member = _members.FirstOrDefault(m => m.UserId == userId);
|
var member = _members.FirstOrDefault(m => m.UserId == userId);
|
||||||
if (member != null) _members.Remove(member);
|
if (member != null)
|
||||||
|
{
|
||||||
|
_members.Remove(member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateName(string name) => Name = name;
|
public void UpdateName(string name) => Name = name;
|
||||||
|
|||||||
+6
-2
@@ -29,7 +29,10 @@ public sealed class MessageSentDomainEventHandler : INotificationHandler<Message
|
|||||||
public async Task Handle(MessageSentDomainEvent notification, CancellationToken cancellationToken)
|
public async Task Handle(MessageSentDomainEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var message = await _messageRepository.GetByIdAsync(notification.MessageId, cancellationToken);
|
var message = await _messageRepository.GetByIdAsync(notification.MessageId, cancellationToken);
|
||||||
if (message is null) return;
|
if (message is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var userInfo = await _displayNameProvider.GetUserInfoAsync(message.SenderId, cancellationToken);
|
var userInfo = await _displayNameProvider.GetUserInfoAsync(message.SenderId, cancellationToken);
|
||||||
var senderObj = userInfo != null
|
var senderObj = userInfo != null
|
||||||
@@ -82,7 +85,8 @@ public sealed class MessageSentDomainEventHandler : INotificationHandler<Message
|
|||||||
message.ReplyToId,
|
message.ReplyToId,
|
||||||
ReplyTo = replyToObj,
|
ReplyTo = replyToObj,
|
||||||
message.Quote,
|
message.Quote,
|
||||||
Media = message.Media.Select(m => new {
|
Media = message.Media.Select(m => new
|
||||||
|
{
|
||||||
m.Id,
|
m.Id,
|
||||||
m.Type,
|
m.Type,
|
||||||
m.Url,
|
m.Url,
|
||||||
|
|||||||
@@ -104,14 +104,20 @@ public sealed class MessageRepository : IMessageRepository
|
|||||||
.AnyAsync(m => m.Id == messageId, cancellationToken);
|
.AnyAsync(m => m.Id == messageId, cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
if (!messageExists) return false;
|
if (!messageExists)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем, есть ли уже такая реакция
|
// Проверяем, есть ли уже такая реакция
|
||||||
var existingReaction = await _dbContext.Reactions
|
var existingReaction = await _dbContext.Reactions
|
||||||
.FirstOrDefaultAsync(r => r.MessageId == messageId && r.UserId == userId && r.Emoji == emoji, cancellationToken);
|
.FirstOrDefaultAsync(r => r.MessageId == messageId && r.UserId == userId && r.Emoji == emoji, cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
if (existingReaction != null) return true; // Уже существует
|
if (existingReaction != null)
|
||||||
|
{
|
||||||
|
return true; // Уже существует
|
||||||
|
}
|
||||||
|
|
||||||
// Добавляем новую реакцию напрямую
|
// Добавляем новую реакцию напрямую
|
||||||
var reaction = new Reaction(messageId, userId, emoji);
|
var reaction = new Reaction(messageId, userId, emoji);
|
||||||
@@ -128,7 +134,10 @@ public sealed class MessageRepository : IMessageRepository
|
|||||||
.FirstOrDefaultAsync(r => r.MessageId == messageId && r.UserId == userId && r.Emoji == emoji, cancellationToken);
|
.FirstOrDefaultAsync(r => r.MessageId == messageId && r.UserId == userId && r.Emoji == emoji, cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
if (reaction == null) return false;
|
if (reaction == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_dbContext.Reactions.Remove(reaction);
|
_dbContext.Reactions.Remove(reaction);
|
||||||
|
|
||||||
|
|||||||
@@ -526,7 +526,8 @@ public sealed class ChatHub : Hub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Clients.Group(request.ChatId).SendAsync("group_call_status_updated", new {
|
await Clients.Group(request.ChatId).SendAsync("group_call_status_updated", new
|
||||||
|
{
|
||||||
chatId = request.ChatId,
|
chatId = request.ChatId,
|
||||||
userId = Context.UserIdentifier,
|
userId = Context.UserIdentifier,
|
||||||
isMuted = request.IsMuted,
|
isMuted = request.IsMuted,
|
||||||
@@ -552,7 +553,8 @@ public sealed class ChatHub : Hub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Clients.Group(chatId).SendAsync("group_call_status_updated", new {
|
await Clients.Group(chatId).SendAsync("group_call_status_updated", new
|
||||||
|
{
|
||||||
chatId = chatId,
|
chatId = chatId,
|
||||||
userId = Context.UserIdentifier,
|
userId = Context.UserIdentifier,
|
||||||
isMuted = isMuted,
|
isMuted = isMuted,
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ internal sealed class AcceptFriendRequestCommandHandler : ICommandHandler<Accept
|
|||||||
{
|
{
|
||||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||||
if (friendship == null || friendship.FriendId != request.UserId)
|
if (friendship == null || friendship.FriendId != request.UserId)
|
||||||
|
{
|
||||||
return Result.Failure<Guid>(new Error("Friends.NotFound", "Request not found"));
|
return Result.Failure<Guid>(new Error("Friends.NotFound", "Request not found"));
|
||||||
|
}
|
||||||
|
|
||||||
friendship.Accept();
|
friendship.Accept();
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ internal sealed class DeclineFriendRequestCommandHandler : ICommandHandler<Decli
|
|||||||
{
|
{
|
||||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||||
if (friendship == null || friendship.FriendId != request.UserId)
|
if (friendship == null || friendship.FriendId != request.UserId)
|
||||||
|
{
|
||||||
return Result.Failure(new Error("Friends.NotFound", "Request not found"));
|
return Result.Failure(new Error("Friends.NotFound", "Request not found"));
|
||||||
|
}
|
||||||
|
|
||||||
friendship.Decline();
|
friendship.Decline();
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ internal sealed class GetFriendsQueryHandler : IQueryHandler<GetFriendsQuery, Li
|
|||||||
foreach (var id in friendIds)
|
foreach (var id in friendIds)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(id, cancellationToken);
|
||||||
if (user == null) continue;
|
if (user == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var fs = friendships.First(f => f.UserId == id || f.FriendId == id);
|
var fs = friendships.First(f => f.UserId == id || f.FriendId == id);
|
||||||
|
|
||||||
|
|||||||
@@ -21,14 +21,18 @@ internal sealed class GetFriendshipStatusQueryHandler : IQueryHandler<GetFriends
|
|||||||
public async Task<Result<FriendshipStatusResponse>> Handle(GetFriendshipStatusQuery request, CancellationToken cancellationToken)
|
public async Task<Result<FriendshipStatusResponse>> Handle(GetFriendshipStatusQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (request.CurrentUserId == request.TargetUserId)
|
if (request.CurrentUserId == request.TargetUserId)
|
||||||
|
{
|
||||||
return Result.Success(new FriendshipStatusResponse("self"));
|
return Result.Success(new FriendshipStatusResponse("self"));
|
||||||
|
}
|
||||||
|
|
||||||
var fs = await _context.Friendships
|
var fs = await _context.Friendships
|
||||||
.FirstOrDefaultAsync(f => (f.UserId == request.CurrentUserId && f.FriendId == request.TargetUserId) ||
|
.FirstOrDefaultAsync(f => (f.UserId == request.CurrentUserId && f.FriendId == request.TargetUserId) ||
|
||||||
(f.UserId == request.TargetUserId && f.FriendId == request.CurrentUserId), cancellationToken);
|
(f.UserId == request.TargetUserId && f.FriendId == request.CurrentUserId), cancellationToken);
|
||||||
|
|
||||||
if (fs == null)
|
if (fs == null)
|
||||||
|
{
|
||||||
return Result.Success(new FriendshipStatusResponse("none"));
|
return Result.Success(new FriendshipStatusResponse("none"));
|
||||||
|
}
|
||||||
|
|
||||||
return Result.Success(new FriendshipStatusResponse(
|
return Result.Success(new FriendshipStatusResponse(
|
||||||
fs.Status.ToString().ToLowerInvariant(),
|
fs.Status.ToString().ToLowerInvariant(),
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ internal sealed class GetIncomingRequestsQueryHandler : IQueryHandler<GetIncomin
|
|||||||
foreach (var fs in friendships)
|
foreach (var fs in friendships)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(fs.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(fs.UserId, cancellationToken);
|
||||||
if (user == null) continue;
|
if (user == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
requestsList.Add(new FriendRequestDto(
|
requestsList.Add(new FriendRequestDto(
|
||||||
fs.Id,
|
fs.Id,
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ internal sealed class GetOutgoingRequestsQueryHandler : IQueryHandler<GetOutgoin
|
|||||||
foreach (var fs in friendships)
|
foreach (var fs in friendships)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(fs.FriendId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(fs.FriendId, cancellationToken);
|
||||||
if (user == null) continue;
|
if (user == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
requestsList.Add(new FriendRequestDto(
|
requestsList.Add(new FriendRequestDto(
|
||||||
fs.Id,
|
fs.Id,
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ internal sealed class RemoveFriendCommandHandler : ICommandHandler<RemoveFriendC
|
|||||||
{
|
{
|
||||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||||
if (friendship == null || (friendship.UserId != request.UserId && friendship.FriendId != request.UserId))
|
if (friendship == null || (friendship.UserId != request.UserId && friendship.FriendId != request.UserId))
|
||||||
|
{
|
||||||
return Result.Failure(new Error("Friends.NotFound", "Friendship not found"));
|
return Result.Failure(new Error("Friends.NotFound", "Friendship not found"));
|
||||||
|
}
|
||||||
|
|
||||||
_context.Friendships.Remove(friendship);
|
_context.Friendships.Remove(friendship);
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -24,14 +24,18 @@ internal sealed class SendFriendRequestCommandHandler : ICommandHandler<SendFrie
|
|||||||
public async Task<Result> Handle(SendFriendRequestCommand request, CancellationToken cancellationToken)
|
public async Task<Result> Handle(SendFriendRequestCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (request.UserId == request.FriendId)
|
if (request.UserId == request.FriendId)
|
||||||
|
{
|
||||||
return Result.Failure(new Error("Friends.Self", "Cannot add yourself"));
|
return Result.Failure(new Error("Friends.Self", "Cannot add yourself"));
|
||||||
|
}
|
||||||
|
|
||||||
var existing = await _context.Friendships
|
var existing = await _context.Friendships
|
||||||
.FirstOrDefaultAsync(f => (f.UserId == request.UserId && f.FriendId == request.FriendId) ||
|
.FirstOrDefaultAsync(f => (f.UserId == request.UserId && f.FriendId == request.FriendId) ||
|
||||||
(f.UserId == request.FriendId && f.FriendId == request.UserId), cancellationToken);
|
(f.UserId == request.FriendId && f.FriendId == request.UserId), cancellationToken);
|
||||||
|
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
|
{
|
||||||
return Result.Failure(new Error("Friends.Exists", "Friendship already exists"));
|
return Result.Failure(new Error("Friends.Exists", "Friendship already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
var friendship = Friendship.Create(request.UserId, request.FriendId);
|
var friendship = Friendship.Create(request.UserId, request.FriendId);
|
||||||
_context.Friendships.Add(friendship);
|
_context.Friendships.Add(friendship);
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ internal sealed class CropAvatarCommandHandler : ICommandHandler<CropAvatarComma
|
|||||||
public async Task<Result<UserProfileDto>> Handle(CropAvatarCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(CropAvatarCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
string avatarUrl;
|
string avatarUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ internal sealed class DeleteAvatarCommandHandler : ICommandHandler<DeleteAvatarC
|
|||||||
public async Task<Result<UserProfileDto>> Handle(DeleteAvatarCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(DeleteAvatarCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
user.UpdateAvatar(null);
|
user.UpdateAvatar(null);
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ internal sealed class UploadAvatarCommandHandler : ICommandHandler<UploadAvatarC
|
|||||||
public async Task<Result<UserProfileDto>> Handle(UploadAvatarCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(UploadAvatarCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
var fileId = await _fileStorage.UploadFileAsync(request.FileStream, request.FileName, request.ContentType);
|
||||||
var avatarUrl = $"/api/files/{fileId}";
|
var avatarUrl = $"/api/files/{fileId}";
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ internal sealed class GetMeQueryHandler : IQueryHandler<GetMeQuery, AuthResponse
|
|||||||
public async Task<Result<AuthResponseDto>> Handle(GetMeQuery request, CancellationToken cancellationToken)
|
public async Task<Result<AuthResponseDto>> Handle(GetMeQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<AuthResponseDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<AuthResponseDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var response = new AuthResponseDto(
|
var response = new AuthResponseDto(
|
||||||
string.Empty,
|
string.Empty,
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ internal sealed class GetUserQueryHandler : IQueryHandler<GetUserQuery, UserProf
|
|||||||
public async Task<Result<UserProfileDto>> Handle(GetUserQuery request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(GetUserQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.Id, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.Id, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
var dto = new UserProfileDto(
|
var dto = new UserProfileDto(
|
||||||
user.Id,
|
user.Id,
|
||||||
|
|||||||
+4
-1
@@ -20,7 +20,10 @@ internal sealed class UpdateProfileCommandHandler : ICommandHandler<UpdateProfil
|
|||||||
public async Task<Result<UserProfileDto>> Handle(UpdateProfileCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(UpdateProfileCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
user.UpdateProfile(request.DisplayName ?? user.DisplayName, request.Bio, request.Birthday);
|
user.UpdateProfile(request.DisplayName ?? user.DisplayName, request.Bio, request.Birthday);
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
+4
-1
@@ -20,7 +20,10 @@ internal sealed class UpdateSettingsCommandHandler : ICommandHandler<UpdateSetti
|
|||||||
public async Task<Result<UserProfileDto>> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
|
public async Task<Result<UserProfileDto>> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
if (user == null) return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Result.Failure<UserProfileDto>(new Error("User.NotFound", "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
user.UpdateSettings(request.HideStoryViews ?? user.HideStoryViews);
|
user.UpdateSettings(request.HideStoryViews ?? user.HideStoryViews);
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -40,20 +40,31 @@ public class Story : Entity<Guid>
|
|||||||
|
|
||||||
public void AddViewer(Guid userId)
|
public void AddViewer(Guid userId)
|
||||||
{
|
{
|
||||||
if (_viewers.Any(v => v.UserId == userId)) return;
|
if (_viewers.Any(v => v.UserId == userId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_viewers.Add(new StoryViewer(Id, userId));
|
_viewers.Add(new StoryViewer(Id, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddReaction(Guid userId, string emoji)
|
public void AddReaction(Guid userId, string emoji)
|
||||||
{
|
{
|
||||||
if (_reactions.Any(r => r.UserId == userId && r.Emoji == emoji)) return;
|
if (_reactions.Any(r => r.UserId == userId && r.Emoji == emoji))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_reactions.Add(new StoryReaction(Id, userId, emoji));
|
_reactions.Add(new StoryReaction(Id, userId, emoji));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveReaction(Guid userId, string emoji)
|
public void RemoveReaction(Guid userId, string emoji)
|
||||||
{
|
{
|
||||||
var reaction = _reactions.FirstOrDefault(r => r.UserId == userId && r.Emoji == emoji);
|
var reaction = _reactions.FirstOrDefault(r => r.UserId == userId && r.Emoji == emoji);
|
||||||
if (reaction != null) _reactions.Remove(reaction);
|
if (reaction != null)
|
||||||
|
{
|
||||||
|
_reactions.Remove(reaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddReply(Guid userId, string content)
|
public void AddReply(Guid userId, string content)
|
||||||
|
|||||||
+5
-1
@@ -21,7 +21,11 @@ public sealed class UserDisplayNameProvider : IUserDisplayNameProvider
|
|||||||
public async Task<UserInfo?> GetUserInfoAsync(Guid userId, CancellationToken ct = default)
|
public async Task<UserInfo?> GetUserInfoAsync(Guid userId, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(userId, ct);
|
var user = await _userRepository.GetByIdAsync(userId, ct);
|
||||||
if (user == null) return null;
|
if (user == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return new UserInfo(user.Id, user.Username, user.DisplayName, user.Avatar);
|
return new UserInfo(user.Id, user.Username, user.DisplayName, user.Avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -29,13 +29,18 @@ public class AesEncryptionService : IEncryptionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_key.Length != 32)
|
if (_key.Length != 32)
|
||||||
|
{
|
||||||
throw new ArgumentException("Мастер-ключ должен быть ровно 32 байта для AES-256.");
|
throw new ArgumentException("Мастер-ключ должен быть ровно 32 байта для AES-256.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Сообщения: AES-256-GCM
|
// Сообщения: AES-256-GCM
|
||||||
public string EncryptMessage(string plainText)
|
public string EncryptMessage(string plainText)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(plainText)) return plainText;
|
if (string.IsNullOrEmpty(plainText))
|
||||||
|
{
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
|
||||||
var nonce = new byte[12];
|
var nonce = new byte[12];
|
||||||
RandomNumberGenerator.Fill(nonce);
|
RandomNumberGenerator.Fill(nonce);
|
||||||
@@ -53,10 +58,16 @@ public class AesEncryptionService : IEncryptionService
|
|||||||
|
|
||||||
public string DecryptMessage(string cipherText)
|
public string DecryptMessage(string cipherText)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(cipherText)) return cipherText;
|
if (string.IsNullOrEmpty(cipherText))
|
||||||
|
{
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
var parts = cipherText.Split(':');
|
var parts = cipherText.Split(':');
|
||||||
if (parts.Length != 3) return cipherText;
|
if (parts.Length != 3)
|
||||||
|
{
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
+7
-1
@@ -70,7 +70,10 @@ public class StatisticsService : IStatisticsService
|
|||||||
|
|
||||||
long onlineUsersCount = _cache.TryGetValue("Global_OnlineUsersCount", out int count) ? count : 0;
|
long onlineUsersCount = _cache.TryGetValue("Global_OnlineUsersCount", out int count) ? count : 0;
|
||||||
long offlineUsersCount = totalUsers - onlineUsersCount;
|
long offlineUsersCount = totalUsers - onlineUsersCount;
|
||||||
if(offlineUsersCount < 0) offlineUsersCount = 0;
|
if (offlineUsersCount < 0)
|
||||||
|
{
|
||||||
|
offlineUsersCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
var stats = new DashboardStatsDto
|
var stats = new DashboardStatsDto
|
||||||
{
|
{
|
||||||
@@ -97,7 +100,10 @@ internal static class SqlExtensions
|
|||||||
{
|
{
|
||||||
cmd.CommandText = "SELECT COUNT(*) FROM identity.\"Users\"";
|
cmd.CommandText = "SELECT COUNT(*) FROM identity.\"Users\"";
|
||||||
if (cmd.Connection?.State != System.Data.ConnectionState.Open)
|
if (cmd.Connection?.State != System.Data.ConnectionState.Open)
|
||||||
|
{
|
||||||
await ((System.Data.Common.DbConnection)cmd.Connection!).OpenAsync();
|
await ((System.Data.Common.DbConnection)cmd.Connection!).OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
var result = await ((System.Data.Common.DbCommand)cmd).ExecuteScalarAsync();
|
var result = await ((System.Data.Common.DbCommand)cmd).ExecuteScalarAsync();
|
||||||
return Convert.ToInt64(result);
|
return Convert.ToInt64(result);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-3
@@ -118,9 +118,20 @@ public class S3FileStorageService : IFileStorageService
|
|||||||
var statArgs = new StatObjectArgs().WithBucket(_bucketName).WithObject(fileId);
|
var statArgs = new StatObjectArgs().WithBucket(_bucketName).WithObject(fileId);
|
||||||
var stat = await _minioClient.StatObjectAsync(statArgs).ConfigureAwait(false);
|
var stat = await _minioClient.StatObjectAsync(statArgs).ConfigureAwait(false);
|
||||||
|
|
||||||
if (stat.MetaData.ContainsKey("Contenttype")) contentType = stat.MetaData["Contenttype"];
|
if (stat.MetaData.ContainsKey("Contenttype"))
|
||||||
if (stat.MetaData.ContainsKey("Originalfilename")) fileName = stat.MetaData["Originalfilename"];
|
{
|
||||||
if (stat.MetaData.ContainsKey("Iv")) ivBase64 = stat.MetaData["Iv"];
|
contentType = stat.MetaData["Contenttype"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.MetaData.ContainsKey("Originalfilename"))
|
||||||
|
{
|
||||||
|
fileName = stat.MetaData["Originalfilename"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.MetaData.ContainsKey("Iv"))
|
||||||
|
{
|
||||||
|
ivBase64 = stat.MetaData["Iv"];
|
||||||
|
}
|
||||||
|
|
||||||
// Загружаем во временный файл (так как CryptoStream требует правильного чтения/записи)
|
// Загружаем во временный файл (так как CryptoStream требует правильного чтения/записи)
|
||||||
var getObjArgs = new GetObjectArgs()
|
var getObjArgs = new GetObjectArgs()
|
||||||
|
|||||||
@@ -21,8 +21,16 @@ public abstract class Entity<TId> : IEquatable<Entity<TId>>
|
|||||||
|
|
||||||
public bool Equals(Entity<TId>? other)
|
public bool Equals(Entity<TId>? other)
|
||||||
{
|
{
|
||||||
if (other is null) return false;
|
if (other is null)
|
||||||
if (ReferenceEquals(this, other)) return true;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(this, other))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return Id.Equals(other.Id);
|
return Id.Equals(other.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ public interface ICommand : IRequest<Result> { }
|
|||||||
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }
|
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }
|
||||||
|
|
||||||
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, Result>
|
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, Result>
|
||||||
where TCommand : ICommand { }
|
where TCommand : ICommand
|
||||||
|
{ }
|
||||||
|
|
||||||
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
|
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
|
||||||
where TCommand : ICommand<TResponse> { }
|
where TCommand : ICommand<TResponse>
|
||||||
|
{ }
|
||||||
|
|||||||
@@ -20,9 +20,14 @@ public class Result
|
|||||||
protected Result(bool isSuccess, Error error)
|
protected Result(bool isSuccess, Error error)
|
||||||
{
|
{
|
||||||
if (isSuccess && error != Error.None)
|
if (isSuccess && error != Error.None)
|
||||||
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
if (!isSuccess && error == Error.None)
|
if (!isSuccess && error == Error.None)
|
||||||
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
IsSuccess = isSuccess;
|
IsSuccess = isSuccess;
|
||||||
Error = error;
|
Error = error;
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ public abstract class ValueObject : IEquatable<ValueObject>
|
|||||||
|
|
||||||
public bool Equals(ValueObject? other)
|
public bool Equals(ValueObject? other)
|
||||||
{
|
{
|
||||||
if (other is null) return false;
|
if (other is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user