Registration rework

+ InvitesRepository
+ AdminsRepository
+ rework jwt
This commit is contained in:
Artemy
2025-06-24 14:27:08 +07:00
parent 410a1ce1cc
commit ce013eae49
37 changed files with 2084 additions and 29 deletions
@@ -0,0 +1,48 @@
using AutoFixture;
using Govor.Core.Models;
using Govor.Data;
using Govor.Data.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Govor.API.Tests.IntegrationTests.EF.Repositories;
[TestFixture]
public class AdminsRepositoryTests
{
private Fixture _fixture;
private DbContextOptions<GovorDbContext> _options;
private int _testIteration = 0;
[SetUp]
public void Setup()
{
_fixture = new Fixture();
_options = new DbContextOptionsBuilder<GovorDbContext>()
.UseInMemoryDatabase(databaseName: $"DbGovor_{nameof(AdminsRepositoryTests)}_{_testIteration}")
.Options;
}
[Test]
public async Task Given_NotEmptyDbSet_When_GetAllAsync_Then_Admins_Are_Returned()
{
// Arrange
var random = new Random();
var admins = _fixture.CreateMany<Admin>(random.Next(3, 10)).ToList();
await using var context = new GovorDbContext(_options);
var repository = new AdminsRepository(context);
context.AddRange(admins);
await context.SaveChangesAsync();
// Act
var result = await repository.GetAllAsync();
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Count(), Is.EqualTo(admins.Count()));
Assert.That(result.Select(r => r.UserId), Is.EquivalentTo(admins.Select(r => r.UserId)));
}
}
@@ -4,6 +4,8 @@ using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Models;
using Govor.Core.Repositories.Users;
using Govor.API.Services.Authentication.Interfaces;
using Govor.Core.Repositories.Admins;
using Govor.Core.Repositories.Invaites;
using Moq;
namespace Govor.API.Tests.UnitTests.Services;
@@ -15,6 +17,9 @@ public class AuthServiceTests
private Mock<IPasswordHasher> _passwordHasherMock;
private Mock<IJwtService> _jwtServiceMock;
private Mock<IUsersRepository> _usersRepositoryMock;
private Mock<IAdminsRepository> _adminsRepositoryMock;
private Mock<IInvitesRepository> _invitesRepositoryMock;
private IAccountService _accountService;
[SetUp]
@@ -29,7 +34,9 @@ public class AuthServiceTests
_accountService = new AuthService(
_usersRepositoryMock.Object,
_jwtServiceMock.Object,
_passwordHasherMock.Object
_passwordHasherMock.Object,
_invitesRepositoryMock.Object,
_adminsRepositoryMock.Object
);
}
@@ -41,7 +48,7 @@ public class AuthServiceTests
// Act & Assert
Assert.ThrowsAsync<UserAlreadyExistException>(async () => await _accountService.RegistrationAsync(
_fixture.Create<string>(), _fixture.Create<string>()));
_fixture.Create<string>(), _fixture.Create<string>(), _fixture.Create<string>()));
}
[Test]
@@ -52,7 +59,7 @@ public class AuthServiceTests
// Act & Assert
Assert.DoesNotThrowAsync(async () => await _accountService.RegistrationAsync(
_fixture.Create<string>(), _fixture.Create<string>()));
_fixture.Create<string>(), _fixture.Create<string>(), _fixture.Create<string>()));
}
[Test]
+6 -6
View File
@@ -20,7 +20,7 @@ public class AuthController : Controller
[HttpPost("register")]// api/auth/register
[RequireHttps]
public async Task<IActionResult> Register([FromBody] UserDto userDto)
public async Task<IActionResult> Register([FromBody] RegistrationDto registrationDto)
{
try
{
@@ -29,25 +29,25 @@ public class AuthController : Controller
return BadRequest(ModelState);
}
var token = await _accountService.RegistrationAsync(userDto.Name, userDto.Password);
_logger.LogInformation($"Register request for {userDto.Name}");
var token = await _accountService.RegistrationAsync(registrationDto.Name, registrationDto.Password, registrationDto.InviteLink);
_logger.LogInformation($"Register request for {registrationDto.Name}");
return Ok(new { token });
}
catch (UserAlreadyExistException ex)
{
_logger.LogWarning(ex, $"Registration failed for user {userDto.Name}");
_logger.LogWarning(ex, $"Registration failed for user {registrationDto.Name}");
return BadRequest("Registration failed: user already exists.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during registration for user {Name}", userDto.Name);
_logger.LogError(ex, "Unexpected error during registration for user {Name}", registrationDto.Name);
return StatusCode(500, "An unexpected error occurred. Please try again later.");
}
}
[HttpPost("login")]// api/auth/login
[RequireHttps]
public async Task<IActionResult> Login([FromBody] UserDto userDto)
public async Task<IActionResult> Login([FromBody] LoginDto userDto)
{
try
{
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Govor.API.Controllers;
[ApiController]
[Route("api/admin/[controller]")]
//[Authorize(Roles = "Admin")]
[Authorize(Roles = "Admin")]
public class UsersController : Controller
{
private readonly ILogger<UsersController> _logger;
+36 -5
View File
@@ -1,3 +1,4 @@
using System.Security.Cryptography;
using System.Text;
using Govor.API.Hubs;
using Govor.API.Services;
@@ -8,6 +9,8 @@ using Govor.API.Services.Authentication.Interfaces;
using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Infrastructure.Validators;
using Govor.Core.Models;
using Govor.Core.Repositories.Admins;
using Govor.Core.Repositories.Invaites;
using Govor.Core.Repositories.MediasAttachments;
using Govor.Core.Repositories.Messages;
using Govor.Core.Repositories.Users;
@@ -16,6 +19,7 @@ using Govor.Data.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
@@ -69,9 +73,6 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();
@@ -84,6 +85,8 @@ builder.Services.AddScoped<IObjectValidator<MediaAttachments>, MediaAttachmentsV
builder.Services.AddScoped<IMessagesRepository, MessagesRepository>();
builder.Services.AddScoped<IMediaAttachmentsRepository, MediaAttachmentsRepository>();
builder.Services.AddScoped<IUsersAdministration, UsersService>();
builder.Services.AddScoped<IInvitesRepository, InvitesRepository>();
builder.Services.AddScoped<IAdminsRepository, AdminsRepository>();
builder.Services.AddDbContext<GovorDbContext>(
options =>
@@ -93,7 +96,32 @@ builder.Services.AddDbContext<GovorDbContext>(
);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Govor API", Version = "v1" });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
Array.Empty<string>()
}
});
});
//builder.Services.AddOpenApi();
var app = builder.Build();
@@ -102,11 +130,12 @@ var app = builder.Build();
if (app.Environment.IsDevelopment())
{
//app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
@@ -117,6 +146,8 @@ app.UseAuthorization();
app.MapControllers();
app.MapHub<ChatsHub>("/api/chats");
app.MapSwagger().RequireAuthorization();
app.Map("/", () => "Not for browsers");
app.Run();
@@ -4,6 +4,8 @@ using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Models;
using Govor.Core.Repositories.Users;
using Govor.API.Services;
using Govor.Core.Repositories.Admins;
using Govor.Core.Repositories.Invaites;
namespace Govor.API.Services.Authentication;
@@ -13,38 +15,64 @@ public class AuthService : IAccountService
private readonly IPasswordHasher _passwordHasher;
private readonly IJwtService _jwtService;
private readonly IUsersRepository _usersRepository;
private readonly IInvitesRepository _invitesRepository;
private readonly IAdminsRepository _adminsRepository;
public AuthService(IUsersRepository usersRepository, IJwtService jwtService, IPasswordHasher passwordHasher)
public AuthService(IUsersRepository usersRepository,
IJwtService jwtService,
IPasswordHasher passwordHasher,
IInvitesRepository invitesRepository,
IAdminsRepository adminsRepository)
{
_usersRepository = usersRepository;
_jwtService = jwtService;
_passwordHasher = passwordHasher;
_invitesRepository = invitesRepository;
_adminsRepository = adminsRepository;
}
public async Task<string> RegistrationAsync(string name, string password)
public async Task<string> RegistrationAsync(string name, string password, string inviteCode)
{
// 1. Проверка существования имени
if (await _usersRepository.ExistsUsernameAsync(name))
throw new UserAlreadyExistException(name);
// 2. Проверка валидности инвайта
var invite = await _invitesRepository.GetByCodeAsync(inviteCode);
// 3. Генерация пароля
var passwordHash = _passwordHasher.Hash(password);
// 4. Создание пользователя
var user = new User
{
Id = Guid.NewGuid(),
Username = name,
Description = string.Empty,
PasswordHash = passwordHash,
CreatedOn = DateOnly.FromDateTime(DateTime.Now),
Description = string.Empty,
CreatedOn = DateOnly.FromDateTime(DateTime.UtcNow),
IconId = Guid.NewGuid(),
WasOnline = DateTime.UtcNow
//Role = role == "Admin" ? "Admin" : "User" // Ограничение ролей
WasOnline = DateTime.UtcNow,
InviteId = invite.Id
};
// 5. Добавление пользователя
await _usersRepository.AddAsync(user);
// 6. Назначение роли, если инвайт — админский
if (invite.IsAdmin)
{
await _adminsRepository.AddAsync(new Admin
{
UserId = user.Id
});
}
// 7. Генерация токена
return _jwtService.GenerateJwtToken(user);
}
public async Task<string> LoginAsync(string name, string password)
{
if (await _usersRepository.ExistsUsernameAsync(name) == false)
@@ -4,6 +4,6 @@ namespace Govor.API.Services.Authentication.Interfaces;
public interface IAccountService
{
public Task<string> RegistrationAsync(string name, string password);
public Task<string> RegistrationAsync(string name, string password, string inviteCode);
public Task<string> LoginAsync(string name, string password);
}
@@ -3,6 +3,8 @@ using System.Security.Claims;
using System.Text;
using Govor.API.Services.Authentication.Interfaces;
using Govor.Core.Models;
using Govor.Core.Repositories.Admins;
using Govor.Core.Repositories.Invaites;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
@@ -11,14 +13,22 @@ namespace Govor.API.Services.Authentication;
public class JwtService : IJwtService
{
private JwtOption _jwtOption;
private IInvitesRepository _invitesRepository;
public JwtService(IOptions<JwtOption> options)
{
_jwtOption = options.Value;
}
public string GenerateJwtToken(User user)
{
Claim[] claims = [new("userID", user.Id.ToString())];
var invite = _invitesRepository.GetByIdAsync(user.InviteId).Result;
var claims = new[]
{
new Claim("userID", user.Id.ToString()),
new Claim(ClaimTypes.Role, invite.IsAdmin ? "Admin" : "User", ClaimValueTypes.String)
};
var singing = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOption.SecretKeу)),
+1 -1
View File
@@ -74,7 +74,7 @@ class Program
{
try
{
UserDto loginData = new UserDto()
RegistrationDto loginData = new RegistrationDto()
{
Name = username,
Password = password
@@ -3,7 +3,7 @@ using Govor.Core.Infrastructure.Validators;
namespace Govor.Core.DTOs;
public record UserDto
public class LoginDto
{
[Required]
[StringLength(UserValidator.MAX_LENGHT_OF_NAME,
+18
View File
@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using Govor.Core.Infrastructure.Validators;
namespace Govor.Core.DTOs;
public record RegistrationDto
{
[Required]
[StringLength(UserValidator.MAX_LENGHT_OF_NAME,
MinimumLength = UserValidator.MIN_LENGHT_OF_NAME,
ErrorMessage = "Username must be between 4 and 50 characters.")]
public string Name { get; init; }
[Required]
[MinLength(8)]
public string Password { get; init; }
[MinLength(8)]
public string InviteLink { get; init; }
}
+10
View File
@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Govor.Core.Models;
public class Admin
{
[Key]
public Guid UserId { get; set; }
public User User { get; set; } = null!;
}
+11
View File
@@ -0,0 +1,11 @@
namespace Govor.Core.Models;
public class Invitation
{
public Guid Id { get; set; }
public bool IsAdmin { get; set; }
public DateTime DateCreated { get; set; }
public DateTime EndDate { get; set; }
public int MaxParticipants { get; set; }
public List<User> Users { get; set; } = new List<User>();
}
+2
View File
@@ -11,4 +11,6 @@ public class User
public Guid IconId {get; set;}
public DateOnly CreatedOn {get; set;}
public DateTime WasOnline {get; set;}
public Guid InviteId {get; set;}
public Invitation? Invite { get; set; }
}
@@ -0,0 +1,9 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Admins;
public interface IAdminsExist
{
bool Exist(Guid guid);
bool Exist(Admin admin);
}
@@ -0,0 +1,9 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Admins;
public interface IAdminsReader
{
Task<List<Admin>> GetAllAsync();
Task<Admin> GetByIdAsync(Guid id);
}
@@ -0,0 +1,6 @@
namespace Govor.Core.Repositories.Admins;
public interface IAdminsRepository : IAdminsReader, IAdminsWriter, IAdminsExist
{
}
@@ -0,0 +1,10 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Admins;
public interface IAdminsWriter
{
Task AddAsync(Admin admin);
Task UpdateAsync(Admin admin);
Task DeleteAsync(Guid admin);
}
@@ -0,0 +1,9 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Invaites;
public interface IInvitesExist
{
bool Exist(Invitation invitation);
bool Exist(Guid guid);
}
@@ -0,0 +1,11 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Invaites;
public interface IInvitesReader
{
Task<List<Invitation>> GetAllAsync();
Task<Invitation> GetByIdAsync(Guid id);
Task<Invitation> GetByCodeAsync(string code);
Task<List<Invitation>> GetAdminsInvitesAsync();
}
@@ -0,0 +1,6 @@
namespace Govor.Core.Repositories.Invaites;
public interface IInvitesRepository : IInvitesReader, IInvitesWriter, IInvitesExist
{
}
@@ -0,0 +1,10 @@
using Govor.Core.Models;
namespace Govor.Core.Repositories.Invaites;
public interface IInvitesWriter
{
Task AddAsync(Invitation invitation);
Task UpdateAsync(Invitation invitation);
Task RemoveAsync(Invitation invitation);
}
@@ -0,0 +1,17 @@
using Govor.Core.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Govor.Data.Configurations;
public class AdminConfiguration : IEntityTypeConfiguration<Admin>
{
public void Configure(EntityTypeBuilder<Admin> builder)
{
builder.HasKey(a => a.UserId);
builder.HasOne(a => a.User)
.WithOne()
.HasForeignKey<Admin>(a => a.UserId);
}
}
@@ -0,0 +1,18 @@
using Govor.Core.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Govor.Data.Configurations;
public class InvitationConfiguration : IEntityTypeConfiguration<Invitation>
{
public void Configure(EntityTypeBuilder<Invitation> builder)
{
builder.HasKey(i => i.Id);
builder.HasMany(i => i.Users)
.WithOne(u => u.Invite)
.HasForeignKey(u => u.InviteId)
.OnDelete(DeleteBehavior.Cascade);
}
}
@@ -9,5 +9,9 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.HasOne(u => u.Invite)
.WithMany(i => i.Users)
.HasForeignKey(u => u.InviteId);
}
}
+6
View File
@@ -8,6 +8,9 @@ namespace Govor.Data;
public class GovorDbContext(DbContextOptions<GovorDbContext> options) : DbContext(options)
{
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Admin> Admins { get; set; }
public virtual DbSet<Invitation> Invitations { get; set; }
public virtual DbSet<Message> Messages { get; set; }
public virtual DbSet<MessageView> MessageViews { get; set; }
@@ -20,6 +23,9 @@ public class GovorDbContext(DbContextOptions<GovorDbContext> options) : DbContex
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new InvitationConfiguration());
modelBuilder.ApplyConfiguration(new AdminConfiguration());
modelBuilder.ApplyConfiguration(new MessagesConfiguration());
modelBuilder.ApplyConfiguration(new MessageReactionConfiguration());
modelBuilder.ApplyConfiguration(new MediaAttachmentsConfiguration());
@@ -0,0 +1,331 @@
// <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("20250624053837_FixAdminRelation")]
partial class FixAdminRelation
{
/// <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.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.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>("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("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.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<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.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.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.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.Message", b =>
{
b.Navigation("MediaAttachments");
b.Navigation("MessageViews");
b.Navigation("Reactions");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,39 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class FixAdminRelation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Admins",
columns: table => new
{
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Admins", x => x.UserId);
table.ForeignKey(
name: "FK_Admins_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Admins");
}
}
}
+375
View File
@@ -0,0 +1,375 @@
// <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("20250624071942_inviteAdd")]
partial class inviteAdd
{
/// <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.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<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone");
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>("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("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.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.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.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");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,69 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class inviteAdd : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "InviteId",
table: "Users",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
migrationBuilder.CreateTable(
name: "Invitations",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
IsAdmin = table.Column<bool>(type: "boolean", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
EndDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
MaxParticipants = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Invitations", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Users_InviteId",
table: "Users",
column: "InviteId");
migrationBuilder.AddForeignKey(
name: "FK_Users_Invitations_InviteId",
table: "Users",
column: "InviteId",
principalTable: "Invitations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Invitations_InviteId",
table: "Users");
migrationBuilder.DropTable(
name: "Invitations");
migrationBuilder.DropIndex(
name: "IX_Users_InviteId",
table: "Users");
migrationBuilder.DropColumn(
name: "InviteId",
table: "Users");
}
}
}
@@ -0,0 +1,375 @@
// <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("20250624072330_FixinviteAdd")]
partial class FixinviteAdd
{
/// <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.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<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone");
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>("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("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.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.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.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");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class FixinviteAdd : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -0,0 +1,375 @@
// <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("20250624072548_InitialCreate")]
partial class InitialCreate
{
/// <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.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<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone");
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>("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("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.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.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.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");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -23,6 +23,16 @@ namespace Govor.Data.Migrations
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")
@@ -89,6 +99,29 @@ namespace Govor.Data.Migrations
b.ToTable("GroupMemberships");
});
modelBuilder.Entity("Govor.Core.Models.Invitation", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone");
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")
@@ -228,6 +261,9 @@ namespace Govor.Data.Migrations
b.Property<Guid>("IconId")
.HasColumnType("uuid");
b.Property<Guid>("InviteId")
.HasColumnType("uuid");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
@@ -241,9 +277,22 @@ namespace Govor.Data.Migrations
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.MediaAttachments", b =>
{
b.HasOne("Govor.Core.Models.Message", "Message")
@@ -293,6 +342,22 @@ namespace Govor.Data.Migrations
.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");
@@ -0,0 +1,50 @@
using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Models;
using Govor.Core.Repositories.Admins;
using Govor.Data.Repositories.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Govor.Data.Repositories;
public class AdminsRepository(GovorDbContext context) : IAdminsRepository
{
private GovorDbContext _context = context;
public async Task<List<Admin>> GetAllAsync()
{
return await _context.Admins
.AsNoTracking()
.Include(a => a.User)
.ToListOrThrowIfEmpty(new NotFoundException("Database is empty"));
}
public Task<Admin> GetByIdAsync(Guid id)
{
throw new NotImplementedException();
}
public Task AddAsync(Admin admin)
{
throw new NotImplementedException();
}
public Task UpdateAsync(Admin admin)
{
throw new NotImplementedException();
}
public Task DeleteAsync(Guid admin)
{
throw new NotImplementedException();
}
public bool Exist(Guid guid)
{
throw new NotImplementedException();
}
public bool Exist(Admin admin)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,52 @@
using Govor.Core.Models;
using Govor.Core.Repositories.Invaites;
namespace Govor.Data.Repositories;
public class InvitesRepository : IInvitesRepository
{
public Task<List<Invitation>> GetAllAsync()
{
throw new NotImplementedException();
}
public Task<Invitation> GetByIdAsync(Guid id)
{
throw new NotImplementedException();
}
public Task<Invitation> GetByCodeAsync(string code)
{
throw new NotImplementedException();
}
public Task<List<Invitation>> GetAdminsInvitesAsync()
{
throw new NotImplementedException();
}
public Task AddAsync(Invitation invitation)
{
throw new NotImplementedException();
}
public Task UpdateAsync(Invitation invitation)
{
throw new NotImplementedException();
}
public Task RemoveAsync(Invitation invitation)
{
throw new NotImplementedException();
}
public bool Exist(Invitation invitation)
{
throw new NotImplementedException();
}
public bool Exist(Guid guid)
{
throw new NotImplementedException();
}
}