Удалены чувствительные данные

This commit is contained in:
Халимов Рустам
2026-03-12 16:01:41 +03:00
parent d025c98e61
commit 782506ccc3
200 changed files with 484563 additions and 4565 deletions

View File

@@ -0,0 +1,57 @@
using Vortex.Modules.Chats.Domain;
using Vortex.Shared.Kernel;
using Vortex.Modules.Chats.Application.Abstractions;
using Microsoft.AspNetCore.SignalR;
using Vortex.Modules.Chats.Infrastructure.SignalR;
using MediatR;
namespace Vortex.Modules.Chats.Application.Messages.React;
public sealed record AddReactionCommand(
Guid MessageId,
Guid UserId,
string Emoji,
Guid ChatId) : ICommand;
public sealed class AddReactionCommandHandler : ICommandHandler<AddReactionCommand>
{
private readonly IMessageRepository _messageRepository;
private readonly IChatsUnitOfWork _unitOfWork;
private readonly IHubContext<ChatHub> _hubContext;
private readonly IUserDisplayNameProvider _displayNameProvider;
public AddReactionCommandHandler(
IMessageRepository messageRepository,
IChatsUnitOfWork unitOfWork,
IHubContext<ChatHub> hubContext,
IUserDisplayNameProvider displayNameProvider)
{
_messageRepository = messageRepository;
_unitOfWork = unitOfWork;
_hubContext = hubContext;
_displayNameProvider = displayNameProvider;
}
public async Task<Result> Handle(AddReactionCommand request, CancellationToken cancellationToken)
{
var message = await _messageRepository.GetByIdAsync(request.MessageId, cancellationToken);
if (message is null) return Result.Failure(new Error("Messages.NotFound", "Message not found."));
message.AddReaction(request.UserId, request.Emoji);
await _unitOfWork.SaveChangesAsync(cancellationToken);
var username = await _displayNameProvider.GetDisplayNameAsync(request.UserId, cancellationToken);
await _hubContext.Clients.Group(request.ChatId.ToString()).SendAsync("reaction_added", new
{
messageId = request.MessageId,
chatId = request.ChatId,
userId = request.UserId,
username = username,
emoji = request.Emoji
});
return Result.Success();
}
}

View File

@@ -0,0 +1,51 @@
using Vortex.Modules.Chats.Domain;
using Vortex.Shared.Kernel;
using Vortex.Modules.Chats.Application.Abstractions;
using Microsoft.AspNetCore.SignalR;
using Vortex.Modules.Chats.Infrastructure.SignalR;
using MediatR;
namespace Vortex.Modules.Chats.Application.Messages.React;
public sealed record RemoveReactionCommand(
Guid MessageId,
Guid UserId,
string Emoji,
Guid ChatId) : ICommand;
public sealed class RemoveReactionCommandHandler : ICommandHandler<RemoveReactionCommand>
{
private readonly IMessageRepository _messageRepository;
private readonly IChatsUnitOfWork _unitOfWork;
private readonly IHubContext<ChatHub> _hubContext;
public RemoveReactionCommandHandler(
IMessageRepository messageRepository,
IChatsUnitOfWork unitOfWork,
IHubContext<ChatHub> hubContext)
{
_messageRepository = messageRepository;
_unitOfWork = unitOfWork;
_hubContext = hubContext;
}
public async Task<Result> Handle(RemoveReactionCommand request, CancellationToken cancellationToken)
{
var message = await _messageRepository.GetByIdAsync(request.MessageId, cancellationToken);
if (message is null) return Result.Failure(new Error("Messages.NotFound", "Message not found."));
message.RemoveReaction(request.UserId, request.Emoji);
await _unitOfWork.SaveChangesAsync(cancellationToken);
await _hubContext.Clients.Group(request.ChatId.ToString()).SendAsync("reaction_removed", new
{
messageId = request.MessageId,
chatId = request.ChatId,
userId = request.UserId,
emoji = request.Emoji
});
return Result.Success();
}
}

View File

@@ -7,15 +7,17 @@ namespace Vortex.Modules.Chats.Application.Messages.Send;
/// <summary>
/// Команда для отправки сообщения в чат.
/// </summary>
public record AttachmentRequest(string Type, string Url, string? FileName, long? FileSize);
public sealed record SendMessageCommand(
Guid ChatId,
Guid SenderId,
string? Content,
string Type,
string? MediaUrl = null,
string? MediaType = null,
string? FileName = null,
long? FileSize = null) : ICommand<Guid>;
List<AttachmentRequest>? Attachments = null,
Guid? ReplyToId = null,
string? Quote = null,
Guid? ForwardedFromId = null) : ICommand<Guid>;
public sealed class SendMessageCommandHandler : ICommandHandler<SendMessageCommand, Guid>
{
@@ -50,11 +52,17 @@ public sealed class SendMessageCommandHandler : ICommandHandler<SendMessageComma
request.ChatId,
request.SenderId,
request.Content,
request.Type);
request.Type,
request.ReplyToId,
request.Quote,
request.ForwardedFromId);
if (!string.IsNullOrEmpty(request.MediaUrl) && !string.IsNullOrEmpty(request.MediaType))
if (request.Attachments != null && request.Attachments.Any())
{
message.AddMedia(request.MediaType, request.MediaUrl, request.FileName, request.FileSize);
foreach (var att in request.Attachments)
{
message.AddMedia(att.Type, att.Url, att.FileName, att.FileSize);
}
}
// 4. Сохраняем

View File

@@ -20,6 +20,7 @@ public sealed class Message : AggregateRoot<Guid>
public string? Quote { get; private set; }
public bool IsEdited { get; private set; }
public bool IsDeleted { get; private set; }
public Guid? ForwardedFromId { get; private set; }
public DateTime CreatedAt { get; private set; }
private readonly List<Media> _media = new();
@@ -28,20 +29,23 @@ public sealed class Message : AggregateRoot<Guid>
private readonly List<ReadReceipt> _readBy = new();
public IReadOnlyCollection<ReadReceipt> ReadBy => _readBy.AsReadOnly();
private Message(Guid id, Guid chatId, Guid senderId, string? content, string type) : base(id)
private Message(Guid id, Guid chatId, Guid senderId, string? content, string type, Guid? replyToId = null, string? quote = null, Guid? forwardedFromId = null) : base(id)
{
ChatId = chatId;
SenderId = senderId;
Content = content;
Type = type;
ReplyToId = replyToId;
Quote = quote;
ForwardedFromId = forwardedFromId;
CreatedAt = DateTime.UtcNow;
RaiseDomainEvent(new MessageSentDomainEvent(Id, ChatId, SenderId, Content));
}
public static Message Create(Guid chatId, Guid senderId, string? content, string type)
public static Message Create(Guid chatId, Guid senderId, string? content, string type, Guid? replyToId = null, string? quote = null, Guid? forwardedFromId = null)
{
return new Message(Guid.NewGuid(), chatId, senderId, content, type);
return new Message(Guid.NewGuid(), chatId, senderId, content, type, replyToId, quote, forwardedFromId);
}
public void AddMedia(string type, string url, string? filename, long? size)
@@ -49,6 +53,27 @@ public sealed class Message : AggregateRoot<Guid>
_media.Add(new Media(Id, type, url, filename, size));
}
private readonly List<Reaction> _reactions = new();
public IReadOnlyCollection<Reaction> Reactions => _reactions.AsReadOnly();
public void AddReaction(Guid userId, string emoji)
{
var existing = _reactions.Find(r => r.UserId == userId && r.Emoji == emoji);
if (existing == null)
{
_reactions.Add(new Reaction(Id, userId, emoji));
}
}
public void RemoveReaction(Guid userId, string emoji)
{
var existing = _reactions.Find(r => r.UserId == userId && r.Emoji == emoji);
if (existing != null)
{
_reactions.Remove(existing);
}
}
public void Edit(string newContent)
{
Content = newContent;
@@ -61,6 +86,22 @@ public sealed class Message : AggregateRoot<Guid>
IsDeleted = true;
}
}
/// <summary>
/// Реакция на сообщение.
/// </summary>
public sealed class Reaction : Entity<Guid>
{
public Guid MessageId { get; private set; }
public Guid UserId { get; private set; }
public string Emoji { get; private set; }
internal Reaction(Guid messageId, Guid userId, string emoji) : base(Guid.NewGuid())
{
MessageId = messageId;
UserId = userId;
Emoji = emoji;
}
}
/// <summary>
/// Медиа-файл, прикрепленный к сообщению.

View File

@@ -36,6 +36,37 @@ public sealed class MessageSentDomainEventHandler : INotificationHandler<Message
? new { Id = userInfo.Id, Username = userInfo.Username, DisplayName = userInfo.DisplayName, Avatar = userInfo.Avatar }
: new { Id = message.SenderId, Username = "unknown", DisplayName = "Unknown", Avatar = (string?)null };
// Fetch forwarded from info if exists
object? forwardedFromObj = null;
if (message.ForwardedFromId.HasValue)
{
var fwdUserInfo = await _displayNameProvider.GetUserInfoAsync(message.ForwardedFromId.Value, cancellationToken);
forwardedFromObj = fwdUserInfo != null
? new { Id = fwdUserInfo.Id, Username = fwdUserInfo.Username, DisplayName = fwdUserInfo.DisplayName, Avatar = fwdUserInfo.Avatar }
: new { Id = message.ForwardedFromId.Value, Username = "unknown", DisplayName = "Unknown", Avatar = (string?)null };
}
// Fetch reply info if exists
object? replyToObj = null;
if (message.ReplyToId.HasValue)
{
var replyMsg = await _messageRepository.GetByIdAsync(message.ReplyToId.Value, cancellationToken);
if (replyMsg != null)
{
var replySenderInfo = await _displayNameProvider.GetUserInfoAsync(replyMsg.SenderId, cancellationToken);
replyToObj = new
{
Id = replyMsg.Id,
Content = replyMsg.Content,
Quote = message.Quote,
media = replyMsg.Media.Select(rm => new { rm.Id, rm.Type, rm.Url }).ToList(),
Sender = replySenderInfo != null
? new { Id = replySenderInfo.Id, Username = replySenderInfo.Username, DisplayName = replySenderInfo.DisplayName }
: new { Id = replyMsg.SenderId, Username = "unknown", DisplayName = "Unknown" }
};
}
}
// Отправляем сообщение в "комнату" чата.
await _hubContext.Clients.Group(notification.ChatId.ToString())
.SendAsync("new_message", new
@@ -46,7 +77,18 @@ public sealed class MessageSentDomainEventHandler : INotificationHandler<Message
message.Content,
message.Type,
message.CreatedAt,
Media = message.Media.ToList(),
message.ForwardedFromId,
ForwardedFrom = forwardedFromObj,
message.ReplyToId,
ReplyTo = replyToObj,
message.Quote,
Media = message.Media.Select(m => new {
m.Id,
m.Type,
m.Url,
Filename = m.Filename,
Size = m.Size
}).ToList(),
Sender = senderObj,
Reactions = new List<object>(),
ReadBy = new List<object>()

View File

@@ -54,6 +54,14 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
mb.HasKey(x => x.Id);
mb.WithOwner().HasForeignKey(x => x.MessageId);
});
builder.OwnsMany(m => m.Reactions, mb =>
{
mb.ToTable("MessageReactions");
mb.HasKey(x => x.Id);
mb.WithOwner().HasForeignKey(x => x.MessageId);
mb.HasIndex(x => new { x.MessageId, x.UserId, x.Emoji }).IsUnique();
});
});
modelBuilder.Entity<ReadReceipt>(builder =>

View File

@@ -22,6 +22,7 @@ public sealed class MessageRepository : IMessageRepository
return await _dbContext.Messages
.Include(m => m.Media)
.Include(m => m.ReadBy)
.Include(m => m.Reactions)
.FirstOrDefaultAsync(m => m.Id == id, cancellationToken);
}
@@ -34,6 +35,7 @@ public sealed class MessageRepository : IMessageRepository
.Take(limit)
.Include(m => m.Media)
.Include(m => m.ReadBy)
.Include(m => m.Reactions)
.ToListAsync(cancellationToken);
}
@@ -50,6 +52,7 @@ public sealed class MessageRepository : IMessageRepository
.Take(50)
.Include(m => m.Media)
.Include(m => m.ReadBy)
.Include(m => m.Reactions)
.ToListAsync(cancellationToken);
}

View File

@@ -0,0 +1,210 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Vortex.Modules.Chats.Infrastructure.Persistence;
#nullable disable
namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(ChatsDbContext))]
[Migration("20260312094130_AddForwardedFromToMessages")]
partial class AddForwardedFromToMessages
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("chats")
.HasAnnotation("ProductVersion", "10.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Chat", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Avatar")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Chats", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("ChatId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid?>("ForwardedFromId")
.HasColumnType("uuid");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<bool>("IsEdited")
.HasColumnType("boolean");
b.Property<string>("Quote")
.HasColumnType("text");
b.Property<Guid?>("ReplyToId")
.HasColumnType("uuid");
b.Property<Guid>("SenderId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Messages", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.ReadReceipt", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<DateTime>("ReadAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("MessageId", "UserId")
.IsUnique();
b.ToTable("ReadReceipts", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Chat", b =>
{
b.OwnsMany("Vortex.Modules.Chats.Domain.ChatMember", "Members", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<Guid>("ChatId")
.HasColumnType("uuid");
b1.Property<bool>("IsMuted")
.HasColumnType("boolean");
b1.Property<DateTime>("JoinedAt")
.HasColumnType("timestamp with time zone");
b1.Property<string>("Role")
.IsRequired()
.HasColumnType("text");
b1.Property<Guid>("UserId")
.HasColumnType("uuid");
b1.HasKey("Id");
b1.HasIndex("ChatId", "UserId")
.IsUnique();
b1.ToTable("ChatMembers", "chats");
b1.WithOwner()
.HasForeignKey("ChatId");
});
b.Navigation("Members");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.OwnsMany("Vortex.Modules.Chats.Domain.Media", "Media", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<string>("Filename")
.HasColumnType("text");
b1.Property<Guid>("MessageId")
.HasColumnType("uuid");
b1.Property<long?>("Size")
.HasColumnType("bigint");
b1.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b1.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b1.HasKey("Id");
b1.HasIndex("MessageId");
b1.ToTable("MessageMedia", "chats");
b1.WithOwner()
.HasForeignKey("MessageId");
});
b.Navigation("Media");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.ReadReceipt", b =>
{
b.HasOne("Vortex.Modules.Chats.Domain.Message", null)
.WithMany("ReadBy")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.Navigation("ReadBy");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddForwardedFromToMessages : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "ForwardedFromId",
schema: "chats",
table: "Messages",
type: "uuid",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ForwardedFromId",
schema: "chats",
table: "Messages");
}
}
}

View File

@@ -0,0 +1,239 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Vortex.Modules.Chats.Infrastructure.Persistence;
#nullable disable
namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(ChatsDbContext))]
[Migration("20260312100324_AddReactionsToMessages")]
partial class AddReactionsToMessages
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("chats")
.HasAnnotation("ProductVersion", "10.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Chat", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Avatar")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Chats", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("ChatId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid?>("ForwardedFromId")
.HasColumnType("uuid");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<bool>("IsEdited")
.HasColumnType("boolean");
b.Property<string>("Quote")
.HasColumnType("text");
b.Property<Guid?>("ReplyToId")
.HasColumnType("uuid");
b.Property<Guid>("SenderId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Messages", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.ReadReceipt", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<DateTime>("ReadAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("MessageId", "UserId")
.IsUnique();
b.ToTable("ReadReceipts", "chats");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Chat", b =>
{
b.OwnsMany("Vortex.Modules.Chats.Domain.ChatMember", "Members", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<Guid>("ChatId")
.HasColumnType("uuid");
b1.Property<bool>("IsMuted")
.HasColumnType("boolean");
b1.Property<DateTime>("JoinedAt")
.HasColumnType("timestamp with time zone");
b1.Property<string>("Role")
.IsRequired()
.HasColumnType("text");
b1.Property<Guid>("UserId")
.HasColumnType("uuid");
b1.HasKey("Id");
b1.HasIndex("ChatId", "UserId")
.IsUnique();
b1.ToTable("ChatMembers", "chats");
b1.WithOwner()
.HasForeignKey("ChatId");
});
b.Navigation("Members");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.OwnsMany("Vortex.Modules.Chats.Domain.Media", "Media", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<string>("Filename")
.HasColumnType("text");
b1.Property<Guid>("MessageId")
.HasColumnType("uuid");
b1.Property<long?>("Size")
.HasColumnType("bigint");
b1.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b1.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b1.HasKey("Id");
b1.HasIndex("MessageId");
b1.ToTable("MessageMedia", "chats");
b1.WithOwner()
.HasForeignKey("MessageId");
});
b.OwnsMany("Vortex.Modules.Chats.Domain.Reaction", "Reactions", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<string>("Emoji")
.IsRequired()
.HasColumnType("text");
b1.Property<Guid>("MessageId")
.HasColumnType("uuid");
b1.Property<Guid>("UserId")
.HasColumnType("uuid");
b1.HasKey("Id");
b1.HasIndex("MessageId", "UserId", "Emoji")
.IsUnique();
b1.ToTable("MessageReactions", "chats");
b1.WithOwner()
.HasForeignKey("MessageId");
});
b.Navigation("Media");
b.Navigation("Reactions");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.ReadReceipt", b =>
{
b.HasOne("Vortex.Modules.Chats.Domain.Message", null)
.WithMany("ReadBy")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.Message", b =>
{
b.Navigation("ReadBy");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddReactionsToMessages : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MessageReactions",
schema: "chats",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
Emoji = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MessageReactions", x => x.Id);
table.ForeignKey(
name: "FK_MessageReactions_Messages_MessageId",
column: x => x.MessageId,
principalSchema: "chats",
principalTable: "Messages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_MessageReactions_MessageId_UserId_Emoji",
schema: "chats",
table: "MessageReactions",
columns: new[] { "MessageId", "UserId", "Emoji" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MessageReactions",
schema: "chats");
}
}
}

View File

@@ -62,6 +62,9 @@ namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid?>("ForwardedFromId")
.HasColumnType("uuid");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
@@ -182,7 +185,36 @@ namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
.HasForeignKey("MessageId");
});
b.OwnsMany("Vortex.Modules.Chats.Domain.Reaction", "Reactions", b1 =>
{
b1.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b1.Property<string>("Emoji")
.IsRequired()
.HasColumnType("text");
b1.Property<Guid>("MessageId")
.HasColumnType("uuid");
b1.Property<Guid>("UserId")
.HasColumnType("uuid");
b1.HasKey("Id");
b1.HasIndex("MessageId", "UserId", "Emoji")
.IsUnique();
b1.ToTable("MessageReactions", "chats");
b1.WithOwner()
.HasForeignKey("MessageId");
});
b.Navigation("Media");
b.Navigation("Reactions");
});
modelBuilder.Entity("Vortex.Modules.Chats.Domain.ReadReceipt", b =>

View File

@@ -76,15 +76,18 @@ public sealed class ChatHub : Hub
[HubMethodName("send_message")]
public async Task SendMessage(SendMessageHubRequest request)
{
var attachments = request.Attachments?.Select(a =>
new AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
var command = new SendMessageCommand(
request.ChatId,
_userContext.UserId,
request.Content,
request.Type,
request.MediaUrl,
request.MediaType,
request.FileName,
request.FileSize);
attachments,
request.ReplyToId,
request.Quote,
request.ForwardedFromId);
await _sender.Send(command);
}
@@ -127,6 +130,22 @@ public sealed class ChatHub : Hub
await Clients.Group(chatId).SendAsync("user_stopped_typing", new { ChatId = chatId, UserId = _userContext.UserId });
}
[HubMethodName("add_reaction")]
public async Task AddReaction(AddReactionRequest request)
{
var command = new Vortex.Modules.Chats.Application.Messages.React.AddReactionCommand(
request.MessageId, _userContext.UserId, request.Emoji, request.ChatId);
await _sender.Send(command);
}
[HubMethodName("remove_reaction")]
public async Task RemoveReaction(RemoveReactionRequest request)
{
var command = new Vortex.Modules.Chats.Application.Messages.React.RemoveReactionCommand(
request.MessageId, _userContext.UserId, request.Emoji, request.ChatId);
await _sender.Send(command);
}
// ────────────────────────────────────────────────────────────────
// WebRTC signaling
// ────────────────────────────────────────────────────────────────
@@ -240,7 +259,15 @@ public sealed class ChatHub : Hub
// Records
// ────────────────────────────────────────────────────────────────
public record SendMessageHubRequest(Guid ChatId, string? Content, string Type, string? MediaUrl, string? MediaType, string? FileName, long? FileSize);
public record AttachmentHubRequest(string Type, string Url, string? FileName, long? FileSize);
public record SendMessageHubRequest(
Guid ChatId,
string? Content,
string Type,
List<AttachmentHubRequest>? Attachments = null,
Guid? ReplyToId = null,
string? Quote = null,
Guid? ForwardedFromId = null);
public record ReadMessagesRequest(Guid ChatId, List<string>? MessageIds);
public record CallOfferRequest(string TargetUserId, object Offer, string CallType, string? ChatId);
public record CallAnswerRequest(string TargetUserId, object Answer);
@@ -249,4 +276,6 @@ public sealed class ChatHub : Hub
public record RenegotiateRequest(string TargetUserId, object Offer);
public record RenegotiateAnswerRequest(string TargetUserId, object Answer);
public record CallTypeChangedRequest(string TargetUserId, string CallType);
public record AddReactionRequest(Guid MessageId, Guid ChatId, string Emoji);
public record RemoveReactionRequest(Guid MessageId, Guid ChatId, string Emoji);
}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Vortex.Modules.Chats")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+50d4d75c04b5524c8439d9168f3a5a578f26d47c")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Modules.Chats")]
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Modules.Chats")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
7ea02b337752b6c3ee9972270dd51a0230d3bf7881178b777a5efe217aceafb1
3f81474107c936fb7023dd1f1d41b8c6e04cc78e956d41fb0e98d29b21aae2c3

View File

@@ -10,7 +10,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Vortex.Modules.Chats
build_property.ProjectDir = E:\GIT\forkmessager\apps\server-net\src\Modules\Chats\
build_property.ProjectDir = e:\GIT\forkmessager\apps\server-net\src\Modules\Chats\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 10.0

View File

@@ -1 +1 @@
c39010166c2f8abf3397d023e78ad2aa480bef5668aa8c99f0daac981b2cf801
361b9f18466c016bfc9d27e7847a8f24463479b03b82ec7866a1124dede24edb

View File

@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj": {}
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj": {}
},
"projects": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectName": "Vortex.Modules.Chats",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -33,8 +33,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}
@@ -518,14 +518,14 @@
}
}
},
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectName": "Vortex.Shared.Kernel",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"

View File

@@ -785,11 +785,11 @@
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectName": "Vortex.Modules.Chats",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -811,8 +811,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}

View File

@@ -1,8 +1,8 @@
{
"version": 2,
"dgSpecHash": "rWL1CDgJNu8=",
"dgSpecHash": "TeczXo+31S0=",
"success": true,
"projectFilePath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"projectFilePath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Chats\\Vortex.Modules.Chats.csproj",
"expectedPackageFiles": [
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr\\12.0.1\\mediatr.12.0.1.nupkg.sha512",
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr.contracts\\2.0.1\\mediatr.contracts.2.0.1.nupkg.sha512",

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Vortex.Modules.Identity")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+50d4d75c04b5524c8439d9168f3a5a578f26d47c")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Modules.Identity")]
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Modules.Identity")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
59b2bed890c47b7093a9de6a348a05d0adb9ad9b7fdd786efe67aaec3a83cae8
c3cadb6f052217ce8055d22cc45365823011381722fc02323060802003a463fa

View File

@@ -10,7 +10,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Vortex.Modules.Identity
build_property.ProjectDir = E:\GIT\forkmessager\apps\server-net\src\Modules\Identity\
build_property.ProjectDir = e:\GIT\forkmessager\apps\server-net\src\Modules\Identity\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 10.0

View File

@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj": {}
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj": {}
},
"projects": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectName": "Vortex.Modules.Identity",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -33,8 +33,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}
@@ -534,14 +534,14 @@
}
}
},
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectName": "Vortex.Shared.Kernel",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"

View File

@@ -1201,11 +1201,11 @@
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectName": "Vortex.Modules.Identity",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -1227,8 +1227,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}

View File

@@ -1,8 +1,8 @@
{
"version": 2,
"dgSpecHash": "zJhKnu5UrIw=",
"dgSpecHash": "LFvHqUy/0DQ=",
"success": true,
"projectFilePath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"projectFilePath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Modules\\Identity\\Vortex.Modules.Identity.csproj",
"expectedPackageFiles": [
"C:\\Users\\HomePC\\.nuget\\packages\\bcrypt.net-next\\4.1.0\\bcrypt.net-next.4.1.0.nupkg.sha512",
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr\\12.0.1\\mediatr.12.0.1.nupkg.sha512",

View File

@@ -0,0 +1,59 @@
using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Vortex.Shared.Infrastructure.Middleware;
public sealed class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception has occurred.");
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
var (statusCode, message) = exception switch
{
UnauthorizedAccessException => (HttpStatusCode.Unauthorized, "Unauthorized access."),
KeyNotFoundException => (HttpStatusCode.NotFound, "Resource not found."),
InvalidOperationException => (HttpStatusCode.BadRequest, exception.Message),
_ => (HttpStatusCode.InternalServerError, "An internal server error occurred.")
};
context.Response.StatusCode = (int)statusCode;
var response = new
{
message = message,
error = message,
detail = exception.Message,
status = context.Response.StatusCode
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}));
}
}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Vortex.Shared.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+50d4d75c04b5524c8439d9168f3a5a578f26d47c")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Shared.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Shared.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
df12f6b69987ce482a3bc4baa321fe1724bb58d2f9dd614e6cb5e69e22c189a3
adb7ba56083ad67b90d8f8374db47603dfc37300aa26f470695f98b925535a7f

View File

@@ -10,7 +10,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Vortex.Shared.Infrastructure
build_property.ProjectDir = E:\GIT\forkmessager\apps\server-net\src\Shared\Vortex.Shared.Infrastructure\
build_property.ProjectDir = e:\GIT\forkmessager\apps\server-net\src\Shared\Vortex.Shared.Infrastructure\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 10.0

View File

@@ -1 +1 @@
ea74af991932cc803d3e09e69b37a4257b9120e69242a97360be1abe5a5d1ad5
97c7b7e62856d30ecc962f5c969927254da81ac762aa00c7fb151631264e6936

View File

@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj": {}
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj": {}
},
"projects": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectName": "Vortex.Shared.Infrastructure",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -33,8 +33,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}
@@ -510,14 +510,14 @@
}
}
},
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectName": "Vortex.Shared.Kernel",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"

View File

@@ -992,11 +992,11 @@
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectName": "Vortex.Shared.Infrastructure",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
@@ -1018,8 +1018,8 @@
"net10.0": {
"targetAlias": "net10.0",
"projectReferences": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj"
}
}
}

View File

@@ -1,8 +1,8 @@
{
"version": 2,
"dgSpecHash": "+h84t9fYbLg=",
"dgSpecHash": "mujERCxuDP8=",
"success": true,
"projectFilePath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"projectFilePath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Infrastructure\\Vortex.Shared.Infrastructure.csproj",
"expectedPackageFiles": [
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr\\12.0.1\\mediatr.12.0.1.nupkg.sha512",
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr.contracts\\2.0.1\\mediatr.contracts.2.0.1.nupkg.sha512",

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Vortex.Shared.Kernel")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+50d4d75c04b5524c8439d9168f3a5a578f26d47c")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Shared.Kernel")]
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Shared.Kernel")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
3df2ff458195b9e6584b2fb1c283dc45733b10adf112306fa915e3c17fd69248
2ab218fedb325bf279a8607f3ddd12468bb20cfa3895570bcfa7cdb1e276425f

View File

@@ -10,7 +10,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Vortex.Shared.Kernel
build_property.ProjectDir = E:\GIT\forkmessager\apps\server-net\src\Shared\Vortex.Shared.Kernel\
build_property.ProjectDir = e:\GIT\forkmessager\apps\server-net\src\Shared\Vortex.Shared.Kernel\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 10.0

View File

@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {}
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {}
},
"projects": {
"E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectUniqueName": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectName": "Vortex.Shared.Kernel",
"projectPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"packagesPath": "C:\\Users\\HomePC\\.nuget\\packages\\",
"outputPath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"outputPath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"

View File

@@ -1,8 +1,8 @@
{
"version": 2,
"dgSpecHash": "qfmzINr+8lA=",
"dgSpecHash": "SdDYXwY4FZU=",
"success": true,
"projectFilePath": "E:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"projectFilePath": "e:\\GIT\\forkmessager\\apps\\server-net\\src\\Shared\\Vortex.Shared.Kernel\\Vortex.Shared.Kernel.csproj",
"expectedPackageFiles": [
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr\\12.0.1\\mediatr.12.0.1.nupkg.sha512",
"C:\\Users\\HomePC\\.nuget\\packages\\mediatr.contracts\\2.0.1\\mediatr.contracts.2.0.1.nupkg.sha512",

View File

@@ -30,37 +30,74 @@ public sealed class MessagesController : ControllerBase
{
var messages = await messageRepository.GetChatMessagesAsync(chatId, 50, 0, ct);
// Получаем всех уникальных отправителей
var senderIds = messages.Select(m => m.SenderId).Distinct().ToList();
// Collect all user IDs needed (senders, forwarded from)
var userIds = messages.Select(m => m.SenderId).ToList();
userIds.AddRange(messages.Where(m => m.ForwardedFromId.HasValue).Select(m => m.ForwardedFromId!.Value));
var senders = new Dictionary<Guid, Vortex.Modules.Identity.Domain.User>();
foreach (var id in senderIds)
foreach (var id in userIds.Distinct())
{
var user = await _userRepository.GetByIdAsync(id, ct);
if (user != null) senders[id] = user;
}
var result = messages.Select(m => new
var result = new List<object>();
foreach (var m in messages)
{
m.Id,
m.ChatId,
m.SenderId,
m.Content,
m.Type,
m.ReplyToId,
m.Quote,
m.IsEdited,
m.IsDeleted,
m.CreatedAt,
Media = m.Media.ToList(),
Sender = senders.TryGetValue(m.SenderId, out var s) ? new {
id = s.Id,
username = s.Username,
displayName = s.DisplayName,
avatar = s.Avatar
} : new { id = m.SenderId, username = "unknown", displayName = "Unknown", avatar = (string?)null },
Reactions = new List<object>(),
ReadBy = m.ReadBy.Select(r => new { userId = r.UserId }).ToList()
});
object? replyToObj = null;
if (m.ReplyToId.HasValue)
{
var replyMsg = await messageRepository.GetByIdAsync(m.ReplyToId.Value, ct);
if (replyMsg != null)
{
var replySender = await _userRepository.GetByIdAsync(replyMsg.SenderId, ct);
replyToObj = new
{
id = replyMsg.Id,
content = replyMsg.Content,
media = replyMsg.Media.Select(rm => new { rm.Id, rm.Type, rm.Url }).ToList(),
sender = replySender != null ? new { id = replySender.Id, username = replySender.Username, displayName = replySender.DisplayName } : null
};
}
}
result.Add(new
{
m.Id,
m.ChatId,
m.SenderId,
m.Content,
m.Type,
m.ReplyToId,
replyTo = replyToObj,
m.Quote,
m.IsEdited,
m.IsDeleted,
m.CreatedAt,
forwardedFromId = m.ForwardedFromId,
forwardedFrom = m.ForwardedFromId.HasValue && senders.TryGetValue(m.ForwardedFromId.Value, out var fwd) ? new {
id = fwd.Id,
username = fwd.Username,
displayName = fwd.DisplayName,
avatar = fwd.Avatar
} : null,
media = m.Media.Select(media => new {
media.Id,
media.Type,
media.Url,
filename = media.Filename,
size = media.Size
}).ToList(),
sender = senders.TryGetValue(m.SenderId, out var s) ? new {
id = s.Id,
username = s.Username,
displayName = s.DisplayName,
avatar = s.Avatar
} : new { id = m.SenderId, username = "unknown", displayName = "Unknown", avatar = (string?)null },
reactions = m.Reactions.Select(r => new { r.UserId, r.Emoji }).ToList(),
readBy = m.ReadBy.Select(r => new { userId = r.UserId }).ToList()
});
}
return Ok(result);
}
@@ -70,9 +107,11 @@ public sealed class MessagesController : ControllerBase
{
var messages = await messageRepository.SearchMessagesAsync(q, chatId, ct);
var senderIds = messages.Select(m => m.SenderId).Distinct().ToList();
var userIds = messages.Select(m => m.SenderId).ToList();
userIds.AddRange(messages.Where(m => m.ForwardedFromId.HasValue).Select(m => m.ForwardedFromId!.Value));
var senders = new Dictionary<Guid, Vortex.Modules.Identity.Domain.User>();
foreach (var id in senderIds)
foreach (var id in userIds.Distinct())
{
var user = await _userRepository.GetByIdAsync(id, ct);
if (user != null) senders[id] = user;
@@ -90,15 +129,28 @@ public sealed class MessagesController : ControllerBase
m.IsEdited,
m.IsDeleted,
m.CreatedAt,
Media = m.Media.ToList(),
Sender = senders.TryGetValue(m.SenderId, out var s) ? new {
forwardedFromId = m.ForwardedFromId,
forwardedFrom = m.ForwardedFromId.HasValue && senders.TryGetValue(m.ForwardedFromId.Value, out var fwd) ? new {
id = fwd.Id,
username = fwd.Username,
displayName = fwd.DisplayName,
avatar = fwd.Avatar
} : null,
media = m.Media.Select(media => new {
media.Id,
media.Type,
media.Url,
filename = media.Filename,
size = media.Size
}).ToList(),
sender = senders.TryGetValue(m.SenderId, out var s) ? new {
id = s.Id,
username = s.Username,
displayName = s.DisplayName,
avatar = s.Avatar
} : new { id = m.SenderId, username = "unknown", displayName = "Unknown", avatar = (string?)null },
Reactions = new List<object>(),
ReadBy = m.ReadBy.Select(r => new { userId = r.UserId }).ToList()
reactions = m.Reactions.Select(r => new { r.UserId, r.Emoji }).ToList(),
readBy = m.ReadBy.Select(r => new { userId = r.UserId }).ToList()
});
return Ok(result);
@@ -126,7 +178,19 @@ public sealed class MessagesController : ControllerBase
[HttpPost("chat/{chatId:guid}")]
public async Task<IActionResult> SendMessage(Guid chatId, [FromBody] SendMessageRequest request)
{
var command = new SendMessageCommand(chatId, _userContext.UserId, request.Content, request.Type);
var attachments = request.Attachments?.Select(a =>
new Vortex.Modules.Chats.Application.Messages.Send.AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
var command = new SendMessageCommand(
chatId,
_userContext.UserId,
request.Content,
request.Type,
attachments,
request.ReplyToId,
request.Quote,
request.ForwardedFromId);
Result<Guid> result = await _sender.Send(command);
if (result.IsFailure)
@@ -138,4 +202,12 @@ public sealed class MessagesController : ControllerBase
}
}
public sealed record SendMessageRequest(string Content, string Type);
public sealed record SendMessageRequest(
string? Content,
string Type,
List<AttachmentDto>? Attachments = null,
Guid? ReplyToId = null,
string? Quote = null,
Guid? ForwardedFromId = null);
public sealed record AttachmentDto(string Type, string Url, string? FileName, long? FileSize);

View File

@@ -18,6 +18,23 @@ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var builder = WebApplication.CreateBuilder(args);
// Регистрация модулей
// Маппинг стандартных переменных окружения в иерархию .NET
var envMappings = new Dictionary<string, string?>
{
["ConnectionStrings:Database"] = builder.Configuration["DATABASE_URL"],
["Jwt:Secret"] = builder.Configuration["JWT_SECRET"],
["Jwt:Issuer"] = builder.Configuration["JWT_ISSUER"],
["Jwt:Audience"] = builder.Configuration["JWT_AUDIENCE"],
["WebRtc:TurnUrl"] = builder.Configuration["TURN_URL"],
["WebRtc:TurnUsername"] = builder.Configuration["TURN_USERNAME"],
["WebRtc:TurnPassword"] = builder.Configuration["TURN_PASSWORD"]
};
// Добавляем только те, что реально заданы в ENV
builder.Configuration.AddInMemoryCollection(
envMappings.Where(kv => !string.IsNullOrEmpty(kv.Value))
.ToDictionary(kv => kv.Key, kv => kv.Value));
builder.Services.AddIdentityModule(builder.Configuration);
builder.Services.AddChatsModule(builder.Configuration);
builder.Services.AddSharedInfrastructure();
@@ -93,6 +110,8 @@ using (var scope = app.Services.CreateScope())
}
// Настройка конвейера запросов
app.UseMiddleware<Vortex.Shared.Infrastructure.Middleware.ExceptionHandlingMiddleware>();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Vortex.Host")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+50d4d75c04b5524c8439d9168f3a5a578f26d47c")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Host")]
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Host")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
d03092ba384d8502d5f7fd21d156b23d210e0799f76ba7d21d44c4557e61517a
7eb1f40e06ff22545aecfb70096f70b5f91da82514bc2bf01247c568eaabc058

View File

@@ -1 +1 @@
cc44608f02d3c58ef2ef3a01f7beeb9810e26622a8244a47488abc4fe1fba02e
11d26d1b87f3679d8be8eeb05168fa71c45a3d83b12aa28eb232183b34639103

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"2XkPbXFkjVRjCUBTJbQq8CDd7pjsUOS1j3gPHS73xDA=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["uOUIBvk7jdHp7c93RIuECwNykpOmiZnVmQxETsngeR0=","ToV71GGCtcAptkaGqQYXeNtDQnAJEljvYAkAKnIRZ\u002Bs=","2fjU\u002BjCBiHEuKj2uQgoWti0oZXkpAdICFty/O9cJSyc=","aTLUi985DoL1wb7QmuhiHoO7J3hi30WEVAUbYVkXxss=","M2rU4lkbWHA97xrUqDaBg8DOdPjKImLmDLqxa\u002BEOspY="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"2XkPbXFkjVRjCUBTJbQq8CDd7pjsUOS1j3gPHS73xDA=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["uOUIBvk7jdHp7c93RIuECwNykpOmiZnVmQxETsngeR0=","ToV71GGCtcAptkaGqQYXeNtDQnAJEljvYAkAKnIRZ\u002Bs=","2fjU\u002BjCBiHEuKj2uQgoWti0oZXkpAdICFty/O9cJSyc=","aTLUi985DoL1wb7QmuhiHoO7J3hi30WEVAUbYVkXxss=","j8oEy769HqBb/9GTCrDSdFySoPpImy4D0XnzBBTzsT0="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"2L4HBRt/ChGO9rohi3wtSDqo95X47fme5m8OHgJIZL0=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["uOUIBvk7jdHp7c93RIuECwNykpOmiZnVmQxETsngeR0=","ToV71GGCtcAptkaGqQYXeNtDQnAJEljvYAkAKnIRZ\u002Bs=","2fjU\u002BjCBiHEuKj2uQgoWti0oZXkpAdICFty/O9cJSyc=","aTLUi985DoL1wb7QmuhiHoO7J3hi30WEVAUbYVkXxss=","M2rU4lkbWHA97xrUqDaBg8DOdPjKImLmDLqxa\u002BEOspY="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"2L4HBRt/ChGO9rohi3wtSDqo95X47fme5m8OHgJIZL0=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["uOUIBvk7jdHp7c93RIuECwNykpOmiZnVmQxETsngeR0=","ToV71GGCtcAptkaGqQYXeNtDQnAJEljvYAkAKnIRZ\u002Bs=","2fjU\u002BjCBiHEuKj2uQgoWti0oZXkpAdICFty/O9cJSyc=","aTLUi985DoL1wb7QmuhiHoO7J3hi30WEVAUbYVkXxss=","j8oEy769HqBb/9GTCrDSdFySoPpImy4D0XnzBBTzsT0="],"CachedAssets":{},"CachedCopyCandidates":{}}

Some files were not shown because too many files have changed in this diff Show More