Новый дизайн, удаление
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Vortex.Modules.Chats.Domain;
|
||||
using Vortex.Modules.Chats.Infrastructure.SignalR;
|
||||
using Vortex.Shared.Kernel;
|
||||
|
||||
namespace Vortex.Modules.Chats.Application.Messages.Delete;
|
||||
|
||||
public sealed record DeleteMessagesCommand(
|
||||
Guid ChatId,
|
||||
Guid UserId,
|
||||
List<Guid> MessageIds,
|
||||
bool DeleteForAll) : global::Vortex.Shared.Kernel.ICommand;
|
||||
|
||||
public sealed class DeleteMessagesCommandHandler : global::Vortex.Shared.Kernel.ICommandHandler<DeleteMessagesCommand>
|
||||
{
|
||||
private readonly IMessageRepository _messageRepository;
|
||||
private readonly Vortex.Modules.Chats.Application.Abstractions.IChatsUnitOfWork _unitOfWork;
|
||||
private readonly IHubContext<ChatHub> _hubContext;
|
||||
|
||||
public DeleteMessagesCommandHandler(
|
||||
IMessageRepository messageRepository,
|
||||
Vortex.Modules.Chats.Application.Abstractions.IChatsUnitOfWork unitOfWork,
|
||||
IHubContext<ChatHub> hubContext)
|
||||
{
|
||||
_messageRepository = messageRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
public async Task<global::Vortex.Shared.Kernel.Result> Handle(DeleteMessagesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var id in request.MessageIds)
|
||||
{
|
||||
var message = await _messageRepository.GetByIdAsync(id, cancellationToken);
|
||||
if (message is null || message.ChatId != request.ChatId) continue;
|
||||
|
||||
if (request.DeleteForAll)
|
||||
{
|
||||
if (message.SenderId == request.UserId)
|
||||
{
|
||||
message.Delete();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message.DeleteForUser(request.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
if (request.DeleteForAll)
|
||||
{
|
||||
await _hubContext.Clients.Group(request.ChatId.ToString()).SendAsync("messages_deleted", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
messageIds = request.MessageIds,
|
||||
deleteForAll = true
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Уведомляем только самого пользователя (все его текущие сессии)
|
||||
await _hubContext.Clients.User(request.UserId.ToString()).SendAsync("messages_deleted", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
messageIds = request.MessageIds,
|
||||
deleteForAll = false
|
||||
});
|
||||
}
|
||||
|
||||
return global::Vortex.Shared.Kernel.Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -34,24 +34,31 @@ public sealed class AddReactionCommandHandler : ICommandHandler<AddReactionComma
|
||||
|
||||
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
|
||||
try
|
||||
{
|
||||
messageId = request.MessageId,
|
||||
chatId = request.ChatId,
|
||||
userId = request.UserId,
|
||||
username = username,
|
||||
emoji = request.Emoji
|
||||
});
|
||||
var message = await _messageRepository.GetByIdAsync(request.MessageId, cancellationToken);
|
||||
if (message is null) return Result.Failure(new Error("Messages.NotFound", "Message not found."));
|
||||
|
||||
return Result.Success();
|
||||
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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure(new Error("AddReaction.Error", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,21 +31,28 @@ public sealed class RemoveReactionCommandHandler : ICommandHandler<RemoveReactio
|
||||
|
||||
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
|
||||
try
|
||||
{
|
||||
messageId = request.MessageId,
|
||||
chatId = request.ChatId,
|
||||
userId = request.UserId,
|
||||
emoji = request.Emoji
|
||||
});
|
||||
var message = await _messageRepository.GetByIdAsync(request.MessageId, cancellationToken);
|
||||
if (message is null) return Result.Failure(new Error("Messages.NotFound", "Message not found."));
|
||||
|
||||
return Result.Success();
|
||||
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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure(new Error("RemoveReaction.Error", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ using Vortex.Shared.Kernel;
|
||||
|
||||
namespace Vortex.Modules.Chats.Domain;
|
||||
|
||||
public sealed record ChatCreatedDomainEvent(Chat Chat) : IDomainEvent;
|
||||
public sealed record ChatMemberAddedDomainEvent(Guid ChatId, Guid UserId) : IDomainEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Тип чата: личный или групповой.
|
||||
/// </summary>
|
||||
@@ -48,7 +51,9 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
/// </summary>
|
||||
public static Chat CreatePersonal()
|
||||
{
|
||||
return new Chat(Guid.NewGuid(), ChatType.Personal, null, null);
|
||||
var chat = new Chat(Guid.NewGuid(), ChatType.Personal, null, null);
|
||||
chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat));
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,7 +61,9 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
/// </summary>
|
||||
public static Chat CreateGroup(string name, string? avatar = null)
|
||||
{
|
||||
return new Chat(Guid.NewGuid(), ChatType.Group, name, avatar);
|
||||
var chat = new Chat(Guid.NewGuid(), ChatType.Group, name, avatar);
|
||||
chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat));
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,13 +71,16 @@ public sealed class Chat : AggregateRoot<Guid>
|
||||
/// </summary>
|
||||
public static Chat Create(string? name, ChatType type, string? avatar = null)
|
||||
{
|
||||
return new Chat(Guid.NewGuid(), type, name, avatar);
|
||||
var chat = new Chat(Guid.NewGuid(), type, name, avatar);
|
||||
chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat));
|
||||
return chat;
|
||||
}
|
||||
|
||||
public void AddMember(Guid userId, string role = "member")
|
||||
{
|
||||
if (_members.Any(m => m.UserId == userId)) return;
|
||||
_members.Add(new ChatMember(Id, userId, role));
|
||||
RaiseDomainEvent(new ChatMemberAddedDomainEvent(Id, userId));
|
||||
}
|
||||
|
||||
public void RemoveMember(Guid userId)
|
||||
|
||||
@@ -29,6 +29,9 @@ public sealed class Message : AggregateRoot<Guid>
|
||||
private readonly List<ReadReceipt> _readBy = new();
|
||||
public IReadOnlyCollection<ReadReceipt> ReadBy => _readBy.AsReadOnly();
|
||||
|
||||
private readonly List<Guid> _deletedByUsers = new();
|
||||
public IReadOnlyCollection<Guid> DeletedByUsers => _deletedByUsers.AsReadOnly();
|
||||
|
||||
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;
|
||||
@@ -84,6 +87,16 @@ public sealed class Message : AggregateRoot<Guid>
|
||||
{
|
||||
Content = null;
|
||||
IsDeleted = true;
|
||||
_media.Clear();
|
||||
_reactions.Clear();
|
||||
}
|
||||
|
||||
public void DeleteForUser(Guid userId)
|
||||
{
|
||||
if (!_deletedByUsers.Contains(userId))
|
||||
{
|
||||
_deletedByUsers.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -101,6 +114,11 @@ public sealed class Reaction : Entity<Guid>
|
||||
UserId = userId;
|
||||
Emoji = emoji;
|
||||
}
|
||||
|
||||
private Reaction() : base(Guid.Empty)
|
||||
{
|
||||
Emoji = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,4 +140,10 @@ public sealed class Media : Entity<Guid>
|
||||
Filename = filename;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
private Media() : base(Guid.Empty)
|
||||
{
|
||||
Type = string.Empty;
|
||||
Url = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Vortex.Modules.Chats.Domain;
|
||||
using Vortex.Modules.Chats.Infrastructure.SignalR;
|
||||
using Vortex.Shared.Kernel;
|
||||
|
||||
namespace Vortex.Modules.Chats.Infrastructure.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Обработчик доменного события создания чата.
|
||||
/// </summary>
|
||||
public sealed class ChatCreatedDomainEventHandler : INotificationHandler<ChatCreatedDomainEvent>, INotificationHandler<ChatMemberAddedDomainEvent>
|
||||
{
|
||||
private readonly IHubContext<ChatHub> _hubContext;
|
||||
private readonly IChatRepository _chatRepository;
|
||||
private readonly IUserDisplayNameProvider _displayNameProvider;
|
||||
|
||||
public ChatCreatedDomainEventHandler(
|
||||
IHubContext<ChatHub> hubContext,
|
||||
IChatRepository chatRepository,
|
||||
IUserDisplayNameProvider displayNameProvider)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
_chatRepository = chatRepository;
|
||||
_displayNameProvider = displayNameProvider;
|
||||
}
|
||||
|
||||
public async Task Handle(ChatCreatedDomainEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await NotifyNewChatAsync(notification.Chat, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task Handle(ChatMemberAddedDomainEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var chat = await _chatRepository.GetByIdAsync(notification.ChatId, cancellationToken);
|
||||
if (chat != null)
|
||||
{
|
||||
// Уведомляем всех участников, что кто-то добавлен (или пользователь сам видит новый чат)
|
||||
await NotifyNewChatAsync(chat, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NotifyNewChatAsync(Chat chat, CancellationToken cancellationToken, string? targetUserId = null)
|
||||
{
|
||||
var members = new List<object>();
|
||||
foreach (var member in chat.Members)
|
||||
{
|
||||
var userInfo = await _displayNameProvider.GetUserInfoAsync(member.UserId, cancellationToken);
|
||||
members.Add(new
|
||||
{
|
||||
member.Id,
|
||||
member.UserId,
|
||||
member.Role,
|
||||
member.JoinedAt,
|
||||
User = userInfo != null
|
||||
? new { userInfo.Id, userInfo.Username, userInfo.DisplayName, userInfo.Avatar, isOnline = false }
|
||||
: new { Id = member.UserId, Username = "unknown", DisplayName = "Unknown", Avatar = (string?)null, isOnline = false }
|
||||
});
|
||||
}
|
||||
|
||||
var payload = new
|
||||
{
|
||||
chat.Id,
|
||||
chat.Type,
|
||||
chat.Name,
|
||||
chat.Avatar,
|
||||
chat.CreatedAt,
|
||||
Members = members,
|
||||
Messages = new List<object>(),
|
||||
UnreadCount = 0
|
||||
};
|
||||
|
||||
if (targetUserId != null)
|
||||
{
|
||||
// Отправляем конкретному пользователю (по всем его соединениям)
|
||||
// Мы можем использовать метод Hub или просто отправить в группу UserId (если она есть)
|
||||
// В нашем ChatHub мы добавляем пользователя в группы чатов при подключении.
|
||||
// Но для нового чата он еще не в группе.
|
||||
// Поэтому отправляем через User ID (SignalR поддерживает Clients.User(id))
|
||||
await _hubContext.Clients.User(targetUserId).SendAsync("new_chat", payload, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Уведомляем всех текущих участников
|
||||
foreach (var member in chat.Members)
|
||||
{
|
||||
await _hubContext.Clients.User(member.UserId.ToString()).SendAsync("new_chat", payload, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using MediatR;
|
||||
using Vortex.Modules.Chats.Domain;
|
||||
using Vortex.Shared.Kernel;
|
||||
@@ -23,6 +24,12 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
|
||||
public DbSet<Chat> Chats => Set<Chat>();
|
||||
public DbSet<Message> Messages => Set<Message>();
|
||||
public DbSet<ReadReceipt> ReadReceipts => Set<ReadReceipt>();
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -40,7 +47,7 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
|
||||
mb.HasKey(m => m.Id);
|
||||
mb.WithOwner().HasForeignKey(m => m.ChatId);
|
||||
mb.HasIndex(m => new { m.ChatId, m.UserId }).IsUnique();
|
||||
});
|
||||
}).Navigation(c => c.Members).UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Message>(builder =>
|
||||
@@ -51,17 +58,26 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
|
||||
builder.OwnsMany(m => m.Media, mb =>
|
||||
{
|
||||
mb.ToTable("MessageMedia");
|
||||
mb.HasKey(x => x.Id);
|
||||
mb.WithOwner().HasForeignKey(x => x.MessageId);
|
||||
});
|
||||
}).Navigation(m => m.Media).UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
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();
|
||||
});
|
||||
}).Navigation(m => m.Reactions).UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
builder.PrimitiveCollection(m => m.DeletedByUsers)
|
||||
.HasColumnName("DeletedByUsers")
|
||||
.UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
builder.HasMany(m => m.ReadBy)
|
||||
.WithOne()
|
||||
.HasForeignKey(r => r.MessageId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Navigation(m => m.ReadBy).UsePropertyAccessMode(PropertyAccessMode.Field);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ReadReceipt>(builder =>
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
// <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("20260312174910_UpdateChatsSchema")]
|
||||
partial class UpdateChatsSchema
|
||||
{
|
||||
/// <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.PrimitiveCollection<Guid[]>("DeletedByUsers")
|
||||
.IsRequired()
|
||||
.HasColumnType("uuid[]")
|
||||
.HasColumnName("DeletedByUsers");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateChatsSchema : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid[]>(
|
||||
name: "DeletedByUsers",
|
||||
schema: "chats",
|
||||
table: "Messages",
|
||||
type: "uuid[]",
|
||||
nullable: false,
|
||||
defaultValue: new Guid[0]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DeletedByUsers",
|
||||
schema: "chats",
|
||||
table: "Messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,11 @@ namespace Vortex.Modules.Chats.Infrastructure.Persistence.Migrations
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.PrimitiveCollection<Guid[]>("DeletedByUsers")
|
||||
.IsRequired()
|
||||
.HasColumnType("uuid[]")
|
||||
.HasColumnName("DeletedByUsers");
|
||||
|
||||
b.Property<Guid?>("ForwardedFromId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ public sealed class ChatHub : Hub
|
||||
{
|
||||
// Маппинг userId → список connectionId
|
||||
private static readonly ConcurrentDictionary<string, HashSet<string>> _userConnections = new();
|
||||
// chatId → (userId → ParticipantInfo)
|
||||
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ParticipantInfo>> _groupCallParticipants = new();
|
||||
|
||||
private readonly ISender _sender;
|
||||
private readonly IUserContext _userContext;
|
||||
@@ -118,6 +120,22 @@ public sealed class ChatHub : Hub
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("delete_messages")]
|
||||
public async Task DeleteMessages(DeleteMessagesHubRequest request)
|
||||
{
|
||||
var parsedIds = request.MessageIds
|
||||
.Select(id => Guid.TryParse(id, out var parsed) ? parsed : Guid.Empty)
|
||||
.Where(id => id != Guid.Empty)
|
||||
.ToList();
|
||||
|
||||
if (parsedIds.Any())
|
||||
{
|
||||
var command = new Vortex.Modules.Chats.Application.Messages.Delete.DeleteMessagesCommand(
|
||||
request.ChatId, _userContext.UserId, parsedIds, request.DeleteForAll);
|
||||
await _sender.Send(command);
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("typing_start")]
|
||||
public async Task TypingStart(string chatId)
|
||||
{
|
||||
@@ -130,12 +148,30 @@ public sealed class ChatHub : Hub
|
||||
await Clients.Group(chatId).SendAsync("user_stopped_typing", new { ChatId = chatId, UserId = _userContext.UserId });
|
||||
}
|
||||
|
||||
[HubMethodName("join_chat")]
|
||||
public async Task JoinChat(string chatId)
|
||||
{
|
||||
// Simple security check: check if user is member of chat (optional but recommended)
|
||||
if (Guid.TryParse(chatId, out var chatGuid))
|
||||
{
|
||||
var userChats = await _chatRepository.GetUserChatsAsync(_userContext.UserId, Context.ConnectionAborted);
|
||||
if (userChats.Any(c => c.Id == chatGuid))
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, chatId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[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);
|
||||
var result = await _sender.Send(command);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
throw new HubException(result.Error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
[HubMethodName("remove_reaction")]
|
||||
@@ -143,7 +179,11 @@ public sealed class ChatHub : Hub
|
||||
{
|
||||
var command = new Vortex.Modules.Chats.Application.Messages.React.RemoveReactionCommand(
|
||||
request.MessageId, _userContext.UserId, request.Emoji, request.ChatId);
|
||||
await _sender.Send(command);
|
||||
var result = await _sender.Send(command);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
throw new HubException(result.Error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
@@ -255,6 +295,150 @@ public sealed class ChatHub : Hub
|
||||
});
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Group Call signals
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
|
||||
[HubMethodName("group_call_join")]
|
||||
public async Task GroupCallJoin(GroupCallJoinRequest request)
|
||||
{
|
||||
var chatId = request.ChatId;
|
||||
var userId = _userContext.UserId.ToString();
|
||||
|
||||
var displayName = Context.User?.FindFirstValue("name") ?? Context.User?.FindFirstValue(ClaimTypes.Name) ?? "User";
|
||||
var avatar = Context.User?.FindFirstValue("avatar");
|
||||
var username = Context.User?.FindFirstValue("unique_name") ?? Context.User?.Identity?.Name ?? "user";
|
||||
|
||||
var userInfo = new ParticipantInfo(userId, username, displayName, avatar);
|
||||
|
||||
var participants = _groupCallParticipants.GetOrAdd(chatId, _ => new ConcurrentDictionary<string, ParticipantInfo>());
|
||||
participants.TryAdd(userId, userInfo);
|
||||
|
||||
// Notify others
|
||||
await Clients.Group(chatId).SendAsync("group_call_user_joined", new {
|
||||
chatId = chatId,
|
||||
userId = userId,
|
||||
userInfo = userInfo
|
||||
});
|
||||
|
||||
// Send current participants to joiner (excluding self)
|
||||
var others = participants.Values.Where(p => p.Id != userId).ToList();
|
||||
await Clients.Caller.SendAsync("group_call_participants", new {
|
||||
chatId = chatId,
|
||||
participants = others
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_call_leave")]
|
||||
public async Task GroupCallLeave(string chatId)
|
||||
{
|
||||
var userId = _userContext.UserId.ToString();
|
||||
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
|
||||
{
|
||||
participants.TryRemove(userId, out _);
|
||||
if (participants.IsEmpty)
|
||||
{
|
||||
_groupCallParticipants.TryRemove(chatId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
await Clients.Group(chatId).SendAsync("group_call_user_left", new {
|
||||
chatId = chatId,
|
||||
userId = userId
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_call_offer")]
|
||||
public async Task GroupCallOffer(GroupCallOfferRequest request)
|
||||
{
|
||||
await SendToUserAsync(request.TargetUserId, "group_call_offer", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
from = _userContext.UserId.ToString(),
|
||||
offer = request.Offer
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_call_answer")]
|
||||
public async Task GroupCallAnswer(GroupCallAnswerRequest request)
|
||||
{
|
||||
await SendToUserAsync(request.TargetUserId, "group_call_answer", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
from = _userContext.UserId.ToString(),
|
||||
answer = request.Answer
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_ice_candidate")]
|
||||
public async Task GroupIceCandidate(GroupIceCandidateRequest request)
|
||||
{
|
||||
await SendToUserAsync(request.TargetUserId, "group_ice_candidate", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
from = _userContext.UserId.ToString(),
|
||||
candidate = request.Candidate
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_call_renegotiate")]
|
||||
public async Task GroupRenegotiate(GroupRenegotiateRequest request)
|
||||
{
|
||||
await SendToUserAsync(request.TargetUserId, "group_call_renegotiate", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
from = _userContext.UserId.ToString(),
|
||||
offer = request.Offer
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("group_call_renegotiate_answer")]
|
||||
public async Task GroupRenegotiateAnswer(GroupRenegotiateAnswerRequest request)
|
||||
{
|
||||
await SendToUserAsync(request.TargetUserId, "group_call_renegotiate_answer", new
|
||||
{
|
||||
chatId = request.ChatId,
|
||||
from = _userContext.UserId.ToString(),
|
||||
answer = request.Answer
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("screen_share_started")]
|
||||
public async Task ScreenShareStarted(string chatId)
|
||||
{
|
||||
var userId = _userContext.UserId.ToString();
|
||||
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
|
||||
{
|
||||
if (participants.TryGetValue(userId, out var info))
|
||||
{
|
||||
participants[userId] = info with { IsSharingScreen = true };
|
||||
}
|
||||
}
|
||||
|
||||
await Clients.Group(chatId).SendAsync("screen_share_started", new {
|
||||
chatId = chatId,
|
||||
userId = userId
|
||||
});
|
||||
}
|
||||
|
||||
[HubMethodName("screen_share_stopped")]
|
||||
public async Task ScreenShareStopped(string chatId)
|
||||
{
|
||||
var userId = _userContext.UserId.ToString();
|
||||
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
|
||||
{
|
||||
if (participants.TryGetValue(userId, out var info))
|
||||
{
|
||||
participants[userId] = info with { IsSharingScreen = false };
|
||||
}
|
||||
}
|
||||
|
||||
await Clients.Group(chatId).SendAsync("screen_share_stopped", new {
|
||||
chatId = chatId,
|
||||
userId = userId
|
||||
});
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Records
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
@@ -278,4 +462,12 @@ public sealed class ChatHub : Hub
|
||||
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);
|
||||
public record DeleteMessagesHubRequest(Guid ChatId, List<string> MessageIds, bool DeleteForAll);
|
||||
public record GroupCallJoinRequest(string ChatId, string CallType);
|
||||
public record ParticipantInfo(string Id, string Username, string DisplayName, string? Avatar, bool IsSharingScreen = false);
|
||||
public record GroupCallOfferRequest(string ChatId, string TargetUserId, object Offer);
|
||||
public record GroupCallAnswerRequest(string ChatId, string TargetUserId, object Answer);
|
||||
public record GroupIceCandidateRequest(string ChatId, string TargetUserId, object Candidate);
|
||||
public record GroupRenegotiateRequest(string ChatId, string TargetUserId, object Offer);
|
||||
public record GroupRenegotiateAnswerRequest(string ChatId, string TargetUserId, object Answer);
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1ec6b438d29413482e1393c8ee5717f1211bd2c1")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Modules.Chats")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Modules.Chats")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
3f81474107c936fb7023dd1f1d41b8c6e04cc78e956d41fb0e98d29b21aae2c3
|
||||
f9ffb401f31277f8b221e98682e16484fff43d34c6ed6bf8ad0e087ed2774d78
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
361b9f18466c016bfc9d27e7847a8f24463479b03b82ec7866a1124dede24edb
|
||||
b343bc938c4e02003d18f33f285c39da0e0ac9c0a7417a27b8b695b2e991a45e
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "TeczXo+31S0=",
|
||||
"dgSpecHash": "rWL1CDgJNu8=",
|
||||
"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",
|
||||
|
||||
@@ -12,6 +12,9 @@ public sealed class Story : Entity<Guid>
|
||||
public DateTime CreatedAt { get; private set; }
|
||||
public DateTime ExpiresAt { get; private set; }
|
||||
|
||||
private readonly List<StoryViewer> _viewers = new();
|
||||
public IReadOnlyCollection<StoryViewer> Viewers => _viewers.AsReadOnly();
|
||||
|
||||
private Story(Guid id, Guid userId, string type, string? mediaUrl, string? content, string? bgColor) : base(id)
|
||||
{
|
||||
UserId = userId;
|
||||
@@ -27,4 +30,10 @@ public sealed class Story : Entity<Guid>
|
||||
{
|
||||
return new Story(Guid.NewGuid(), userId, type, mediaUrl, content, bgColor);
|
||||
}
|
||||
|
||||
public void AddViewer(Guid userId)
|
||||
{
|
||||
if (_viewers.Any(v => v.UserId == userId)) return;
|
||||
_viewers.Add(new StoryViewer(Id, userId));
|
||||
}
|
||||
}
|
||||
|
||||
17
apps/server-net/src/Modules/Identity/Domain/StoryViewer.cs
Normal file
17
apps/server-net/src/Modules/Identity/Domain/StoryViewer.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Vortex.Shared.Kernel;
|
||||
|
||||
namespace Vortex.Modules.Identity.Domain;
|
||||
|
||||
public sealed class StoryViewer : Entity<Guid>
|
||||
{
|
||||
public Guid StoryId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public DateTime ViewedAt { get; private set; }
|
||||
|
||||
internal StoryViewer(Guid storyId, Guid userId) : base(Guid.NewGuid())
|
||||
{
|
||||
StoryId = storyId;
|
||||
UserId = userId;
|
||||
ViewedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Vortex.Shared.Kernel;
|
||||
using Vortex.Modules.Identity.Domain;
|
||||
|
||||
@@ -22,7 +23,14 @@ public sealed class IdentityDbContext : DbContext, IIdentityUnitOfWork
|
||||
|
||||
public DbSet<User> Users => Set<User>();
|
||||
public DbSet<Story> Stories => Set<Story>();
|
||||
public DbSet<StoryViewer> StoryViewers => Set<StoryViewer>();
|
||||
public DbSet<Friendship> Friendships => Set<Friendship>();
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -40,6 +48,17 @@ public sealed class IdentityDbContext : DbContext, IIdentityUnitOfWork
|
||||
builder.ToTable("Stories");
|
||||
builder.HasKey(s => s.Id);
|
||||
builder.Property(s => s.Type).IsRequired();
|
||||
builder.HasMany(s => s.Viewers)
|
||||
.WithOne()
|
||||
.HasForeignKey(v => v.StoryId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<StoryViewer>(builder =>
|
||||
{
|
||||
builder.ToTable("StoryViewers");
|
||||
builder.HasKey(v => v.Id);
|
||||
builder.HasIndex(v => new { v.StoryId, v.UserId }).IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<User>(builder =>
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// <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.Identity.Infrastructure.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vortex.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(IdentityDbContext))]
|
||||
[Migration("20260312174453_AddHideStoryViewsAndFixStoryViewer")]
|
||||
partial class AddHideStoryViewsAndFixStoryViewer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("identity")
|
||||
.HasAnnotation("ProductVersion", "10.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.Friendship", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("FriendId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "FriendId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Friendships", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.Story", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("BgColor")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("MediaUrl")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Stories", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.StoryViewer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("StoryId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("ViewedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoryId", "UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StoryViewers", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
||||
b.Property<string>("Bio")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
||||
b.Property<DateTime?>("Birthday")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<bool>("HideStoryViews")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.StoryViewer", b =>
|
||||
{
|
||||
b.HasOne("Vortex.Modules.Identity.Domain.Story", null)
|
||||
.WithMany("Viewers")
|
||||
.HasForeignKey("StoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.Story", b =>
|
||||
{
|
||||
b.Navigation("Viewers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vortex.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddHideStoryViewsAndFixStoryViewer : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "StoryViewers",
|
||||
schema: "identity",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
StoryId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ViewedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_StoryViewers", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_StoryViewers_Stories_StoryId",
|
||||
column: x => x.StoryId,
|
||||
principalSchema: "identity",
|
||||
principalTable: "Stories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StoryViewers_StoryId_UserId",
|
||||
schema: "identity",
|
||||
table: "StoryViewers",
|
||||
columns: new[] { "StoryId", "UserId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "StoryViewers",
|
||||
schema: "identity");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,29 @@ namespace Vortex.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Stories", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.StoryViewer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("StoryId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("ViewedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoryId", "UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StoryViewers", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -132,6 +155,20 @@ namespace Vortex.Modules.Identity.Infrastructure.Persistence.Migrations
|
||||
|
||||
b.ToTable("Users", "identity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.StoryViewer", b =>
|
||||
{
|
||||
b.HasOne("Vortex.Modules.Identity.Domain.Story", null)
|
||||
.WithMany("Viewers")
|
||||
.HasForeignKey("StoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vortex.Modules.Identity.Domain.Story", b =>
|
||||
{
|
||||
b.Navigation("Viewers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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+d025c98e61cc08a53e6ebc62544d35be73a039e7")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1ec6b438d29413482e1393c8ee5717f1211bd2c1")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Vortex.Modules.Identity")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Vortex.Modules.Identity")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
c3cadb6f052217ce8055d22cc45365823011381722fc02323060802003a463fa
|
||||
9d9ffd258fd1015aa25ef1b7c83a7e79e77e5f90a31d4742aa8a559cc7698555
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
9c4a98722858a19d09e1cea0910af6e8695a81fc69a145e71388b513aa5d75c8
|
||||
f5710cf9cae3bcef5bc636925e0d10679b38fdce0a7e8d6824b7b47ab526589d
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "LFvHqUy/0DQ=",
|
||||
"dgSpecHash": "zJhKnu5UrIw=",
|
||||
"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",
|
||||
|
||||
Reference in New Issue
Block a user