FriendsController

This commit is contained in:
Artemy
2025-06-27 19:12:31 +07:00
parent ec44347bbe
commit 05298c004c
17 changed files with 1008 additions and 22 deletions
@@ -0,0 +1,467 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Govor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Govor.Data.Migrations
{
[DbContext(typeof(GovorDbContext))]
[Migration("20250627101322_friendships")]
partial class friendships
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Govor.Core.Models.Admin", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("UserId");
b.ToTable("Admins");
});
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.PrimitiveCollection<List<Guid>>("Admins")
.IsRequired()
.HasColumnType("uuid[]");
b.PrimitiveCollection<List<string>>("InviteCode")
.IsRequired()
.HasColumnType("text[]");
b.Property<bool>("IsChannel")
.HasColumnType("boolean");
b.Property<bool>("IsPrivate")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ChatGroups");
});
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AddresseeId")
.HasColumnType("uuid");
b.Property<Guid>("RequesterId")
.HasColumnType("uuid");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AddresseeId");
b.HasIndex("RequesterId");
b.ToTable("Friendships");
});
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("GroupId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("GroupAdmins");
});
modelBuilder.Entity("Govor.Core.Models.GroupMembership", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("GroupId")
.HasColumnType("uuid");
b.Property<bool>("IsBanned")
.HasColumnType("boolean");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("GroupMemberships");
});
modelBuilder.Entity("Govor.Core.Models.Invitation", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsActive")
.HasColumnType("boolean");
b.Property<bool>("IsAdmin")
.HasColumnType("boolean");
b.Property<int>("MaxParticipants")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Invitations");
});
modelBuilder.Entity("Govor.Core.Models.MediaAttachments", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("EncryptedKey")
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<string>("FilePath")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<string>("MimeType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("MediaAttachments");
});
modelBuilder.Entity("Govor.Core.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("EditedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("EncryptedContent")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsEdited")
.HasColumnType("boolean");
b.Property<Guid?>("PrivateChatId")
.HasColumnType("uuid");
b.Property<Guid>("RecipientId")
.HasColumnType("uuid");
b.Property<int>("RecipientType")
.HasColumnType("integer");
b.Property<Guid?>("ReplyToMessageId")
.HasColumnType("uuid");
b.Property<Guid>("SenderId")
.HasColumnType("uuid");
b.Property<DateTime>("SentAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("PrivateChatId");
b.HasIndex("ReplyToMessageId");
b.ToTable("Messages");
});
modelBuilder.Entity("Govor.Core.Models.MessageReaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<DateTime>("ReactedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("ReactionCode")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("MessageId", "UserId")
.IsUnique();
b.ToTable("MessageReactions");
});
modelBuilder.Entity("Govor.Core.Models.MessageView", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("MessageId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<DateTime>("ViewedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("MessageId", "UserId")
.IsUnique();
b.ToTable("MessageViews");
});
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("UserAId")
.HasColumnType("uuid");
b.Property<Guid>("UserBId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("PrivateChats");
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateOnly>("CreatedOn")
.HasColumnType("date");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("IconId")
.HasColumnType("uuid");
b.Property<Guid>("InviteId")
.HasColumnType("uuid");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("WasOnline")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("InviteId");
b.ToTable("Users");
});
modelBuilder.Entity("Govor.Core.Models.Admin", b =>
{
b.HasOne("Govor.Core.Models.User", "User")
.WithOne()
.HasForeignKey("Govor.Core.Models.Admin", "UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
{
b.HasOne("Govor.Core.Models.User", "Addressee")
.WithMany("ReceivedFriendRequests")
.HasForeignKey("AddresseeId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Govor.Core.Models.User", "Requester")
.WithMany("SentFriendRequests")
.HasForeignKey("RequesterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Addressee");
b.Navigation("Requester");
});
modelBuilder.Entity("Govor.Core.Models.MediaAttachments", b =>
{
b.HasOne("Govor.Core.Models.Message", "Message")
.WithMany("MediaAttachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Message");
});
modelBuilder.Entity("Govor.Core.Models.Message", b =>
{
b.HasOne("Govor.Core.Models.PrivateChat", null)
.WithMany("Messages")
.HasForeignKey("PrivateChatId");
b.HasOne("Govor.Core.Models.Message", "ReplyToMessage")
.WithMany()
.HasForeignKey("ReplyToMessageId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("ReplyToMessage");
});
modelBuilder.Entity("Govor.Core.Models.MessageReaction", b =>
{
b.HasOne("Govor.Core.Models.Message", "Message")
.WithMany("Reactions")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Govor.Core.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Message");
b.Navigation("User");
});
modelBuilder.Entity("Govor.Core.Models.MessageView", b =>
{
b.HasOne("Govor.Core.Models.Message", null)
.WithMany("MessageViews")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.HasOne("Govor.Core.Models.Invitation", "Invite")
.WithMany("Users")
.HasForeignKey("InviteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Invite");
});
modelBuilder.Entity("Govor.Core.Models.Invitation", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Govor.Core.Models.Message", b =>
{
b.Navigation("MediaAttachments");
b.Navigation("MessageViews");
b.Navigation("Reactions");
});
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
{
b.Navigation("Messages");
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.Navigation("ReceivedFriendRequests");
b.Navigation("SentFriendRequests");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,104 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class friendships : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "PrivateChatId",
table: "Messages",
type: "uuid",
nullable: true);
migrationBuilder.CreateTable(
name: "Friendships",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
RequesterId = table.Column<Guid>(type: "uuid", nullable: false),
AddresseeId = table.Column<Guid>(type: "uuid", nullable: false),
Status = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Friendships", x => x.Id);
table.ForeignKey(
name: "FK_Friendships_Users_AddresseeId",
column: x => x.AddresseeId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Friendships_Users_RequesterId",
column: x => x.RequesterId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "PrivateChats",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserAId = table.Column<Guid>(type: "uuid", nullable: false),
UserBId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PrivateChats", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Messages_PrivateChatId",
table: "Messages",
column: "PrivateChatId");
migrationBuilder.CreateIndex(
name: "IX_Friendships_AddresseeId",
table: "Friendships",
column: "AddresseeId");
migrationBuilder.CreateIndex(
name: "IX_Friendships_RequesterId",
table: "Friendships",
column: "RequesterId");
migrationBuilder.AddForeignKey(
name: "FK_Messages_PrivateChats_PrivateChatId",
table: "Messages",
column: "PrivateChatId",
principalTable: "PrivateChats",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Messages_PrivateChats_PrivateChatId",
table: "Messages");
migrationBuilder.DropTable(
name: "Friendships");
migrationBuilder.DropTable(
name: "PrivateChats");
migrationBuilder.DropIndex(
name: "IX_Messages_PrivateChatId",
table: "Messages");
migrationBuilder.DropColumn(
name: "PrivateChatId",
table: "Messages");
}
}
}
@@ -62,6 +62,30 @@ namespace Govor.Data.Migrations
b.ToTable("ChatGroups");
});
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AddresseeId")
.HasColumnType("uuid");
b.Property<Guid>("RequesterId")
.HasColumnType("uuid");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AddresseeId");
b.HasIndex("RequesterId");
b.ToTable("Friendships");
});
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
{
b.Property<Guid>("Id")
@@ -181,6 +205,9 @@ namespace Govor.Data.Migrations
b.Property<bool>("IsEdited")
.HasColumnType("boolean");
b.Property<Guid?>("PrivateChatId")
.HasColumnType("uuid");
b.Property<Guid>("RecipientId")
.HasColumnType("uuid");
@@ -198,6 +225,8 @@ namespace Govor.Data.Migrations
b.HasKey("Id");
b.HasIndex("PrivateChatId");
b.HasIndex("ReplyToMessageId");
b.ToTable("Messages");
@@ -256,6 +285,23 @@ namespace Govor.Data.Migrations
b.ToTable("MessageViews");
});
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("UserAId")
.HasColumnType("uuid");
b.Property<Guid>("UserBId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("PrivateChats");
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.Property<Guid>("Id")
@@ -304,6 +350,25 @@ namespace Govor.Data.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
{
b.HasOne("Govor.Core.Models.User", "Addressee")
.WithMany("ReceivedFriendRequests")
.HasForeignKey("AddresseeId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Govor.Core.Models.User", "Requester")
.WithMany("SentFriendRequests")
.HasForeignKey("RequesterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Addressee");
b.Navigation("Requester");
});
modelBuilder.Entity("Govor.Core.Models.MediaAttachments", b =>
{
b.HasOne("Govor.Core.Models.Message", "Message")
@@ -317,6 +382,10 @@ namespace Govor.Data.Migrations
modelBuilder.Entity("Govor.Core.Models.Message", b =>
{
b.HasOne("Govor.Core.Models.PrivateChat", null)
.WithMany("Messages")
.HasForeignKey("PrivateChatId");
b.HasOne("Govor.Core.Models.Message", "ReplyToMessage")
.WithMany()
.HasForeignKey("ReplyToMessageId")
@@ -377,6 +446,18 @@ namespace Govor.Data.Migrations
b.Navigation("Reactions");
});
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
{
b.Navigation("Messages");
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.Navigation("ReceivedFriendRequests");
b.Navigation("SentFriendRequests");
});
#pragma warning restore 612, 618
}
}
@@ -22,6 +22,9 @@ public class UsersRepository : IUsersRepository
{
return await _context.Users
.AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.ToListOrThrowIfEmpty(new NotFoundException("Users in Database not exists"));
}
@@ -33,6 +36,8 @@ public class UsersRepository : IUsersRepository
return await _context.Users
.AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.FirstOrDefaultAsync(x => x.Id == id)
?? throw new NotFoundByKeyException<Guid>(id, "User with given id does not exist");
}
@@ -46,6 +51,8 @@ public class UsersRepository : IUsersRepository
.AsNoTracking()
.Where(x => ids.Contains(x.Id))
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<Guid>>(ids,"Users with given ids not found"));
}
@@ -57,10 +64,30 @@ public class UsersRepository : IUsersRepository
return await _context.Users
.AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.FirstOrDefaultAsync(x => x.Username == username)
?? throw new NotFoundByKeyException<string>(username, "User with given username does not exist");
}
public async Task<List<User>> SearchPotentialFriendsAsync(Guid currentUserId, string query)
{
return await _context.Users
.AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.Where(u => u.Id != currentUserId &&
u.Username.ToLower().Contains(query.ToLower()) &&
!_context.Friendships.Any(f =>
(f.RequesterId == currentUserId && f.AddresseeId == u.Id) ||
(f.RequesterId == u.Id && f.AddresseeId == currentUserId)))
.Take(20)
.OrderBy(u => u.Username)
.ToListAsync();
}
public async Task<List<User>> FindByRangeUsernamesAsync(IEnumerable<string> usernames)
{
if (usernames is null || !usernames.Any())
@@ -70,6 +97,8 @@ public class UsersRepository : IUsersRepository
.AsNoTracking()
.Where(x => usernames.Contains(x.Username))
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<string>>(usernames, "Users with given usernames not found"));
}
@@ -82,6 +111,8 @@ public class UsersRepository : IUsersRepository
.AsNoTracking()
.Where(u => createdDate == u.CreatedOn)
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.ToListOrThrowIfEmpty(new NotFoundByKeyException<DateOnly>(createdDate, "Users with given created date do not exist"));
}