Invitation globale work

+ tests
+ new services
+ InvitationDto and IInvitationReqest
This commit is contained in:
Artemy
2025-06-24 21:20:43 +07:00
parent f99ec0b17b
commit 76094af15b
38 changed files with 483 additions and 1640 deletions
@@ -204,12 +204,14 @@ public class InvitesRepositoryTests
// Act
var result = repository.Exist(Guid.NewGuid());
var result2 = repository.Exist(_fixture.Create<string>());
Assert.That(result, Is.False);
Assert.That(result2, Is.False);
}
[Test]
public async Task Given_ExistInvitesCode_When_Exist_Then_Returns_False()
public async Task Given_ExistInvites_When_Exist_Then_Returns_False()
{
// Arrange
var invitation = _fixture.Create<Invitation>();
@@ -226,11 +228,14 @@ public class InvitesRepositoryTests
// Act
var result = repository.Exist(invitation.Id);
var result2 = repository.Exist(invitation);
var result3 = repository.Exist(invitation.Code);
Assert.That(result, Is.True);
Assert.That(result2, Is.True);
Assert.That(result3, Is.True);
}
[Test]
public async Task Given_InvalidInvites_When_Exist_Then_Returns_False()
{
@@ -67,23 +67,24 @@ public class InvitationValidatorTests
}
[Test]
public void Given_InvalidEndDate_When_Exist_Then_Returns_False()
public void Given_InvalidEndDatesAndIsActive_When_Exist_Then_Returns_False()
{
// Arrange
var invitation = _fixture.Create<Invitation>();
invitation.EndDate = invitation.DateCreated.AddDays(-1);
invitation.IsActive = true;
// Act & Assert
Assert.Throws<InvalidObjectException<Invitation>>( () => _messageValidator.Validate(invitation));
Assert.That(_messageValidator.TryValidate(invitation), Is.False);
}
[Test]
public void Given_InvalidMaxParticipants_When_Exist_Then_Returns_False()
public void Given_InvalidMaxParticipantsAndIsActive_When_Exist_Then_Returns_False()
{
// Arrange
var invitation = _fixture.Create<Invitation>();
invitation.MaxParticipants = new Random().Next(-5, 0);
invitation.IsActive = true;
// Act & Assert
Assert.Throws<InvalidObjectException<Invitation>>( () => _messageValidator.Validate(invitation));
@@ -18,7 +18,6 @@ public class AuthServiceTests
private Mock<IJwtService> _jwtServiceMock;
private Mock<IUsersRepository> _usersRepositoryMock;
private Mock<IAdminsRepository> _adminsRepositoryMock;
private Mock<IInvitesRepository> _invitesRepositoryMock;
private IAccountService _accountService;
@@ -27,15 +26,22 @@ public class AuthServiceTests
{
_fixture = new Fixture();
_fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => _fixture.Behaviors.Remove(b));
_fixture.Behaviors.Add(new OmitOnRecursionBehavior());
_usersRepositoryMock = new Mock<IUsersRepository>();
_passwordHasherMock = new Mock<IPasswordHasher>();
_jwtServiceMock = new Mock<IJwtService>();
_adminsRepositoryMock = new Mock<IAdminsRepository>();
_accountService = new AuthService(
_usersRepositoryMock.Object,
_jwtServiceMock.Object,
_passwordHasherMock.Object,
_invitesRepositoryMock.Object,
_adminsRepositoryMock.Object
);
}
@@ -48,7 +54,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>(), _fixture.Create<Invitation>()));
}
[Test]
@@ -59,7 +65,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>(), _fixture.Create<Invitation>()));
}
[Test]
@@ -0,0 +1,77 @@
using AutoMapper;
using Govor.API.Services.AdminsStuff.Interfaces;
using Govor.Core.DTOs;
using Govor.Core.Repositories.Invaites;
using Govor.Core.Requests;
using Microsoft.AspNetCore.Mvc;
namespace Govor.API.Controllers.AdminStuff;
[Route("api/[controller]")]
public class InviteUserController : Controller
{
private readonly IInvitesRepository _repository;
private readonly IInvitationGenerator _invitationGenerator;
private readonly ILogger<InviteUserController> _logger;
public InviteUserController(IInvitationGenerator invitationGenerator,
IInvitesRepository repository,
ILogger<InviteUserController> logger)
{
_invitationGenerator = invitationGenerator;
_logger = logger;
_repository = repository;
}
[HttpPost("[action]")]
public async Task<IActionResult> Invitation([FromBody] CreateInvitationRequest createInvitation)
{
try
{
var result = await _invitationGenerator.GenerateInvitationCode(createInvitation.EndDate,
createInvitation.MaxParticipants,
createInvitation.IsAdmin,
createInvitation.Description);
return Ok(result);
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
return BadRequest($"An error occured: {e.Message}");
}
}
[HttpGet]
public async Task<IActionResult> GetAllInvitations()
{
try
{
_logger.LogInformation("Getting all invitations by administrator");
var read = await _repository.GetAllAsync();
List<InvitationDto> dto = new List<InvitationDto>();
foreach (var inv in read)
{
dto.Add(new InvitationDto(){
Id = inv.Id,
Description = inv.Description,
IsAdmin = inv.IsAdmin,
MaxParticipants = inv.MaxParticipants,
Code = inv.Code,
CreatedAt = inv.DateCreated,
EndAt = inv.EndDate,
IsActive = inv.IsActive,
});
}
return Ok(dto);
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
return BadRequest($"An error occured: {e.Message}");
}
}
}
@@ -2,7 +2,7 @@ using Govor.API.Services.AdminsStuff.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Govor.API.Controllers;
namespace Govor.API.Controllers.AdminStuff;
[ApiController]
@@ -12,13 +12,13 @@ public class UsersController : Controller
{
private readonly ILogger<UsersController> _logger;
private readonly IUsersAdministration _users;
public UsersController(ILogger<UsersController> logger, IUsersAdministration users)
public UsersController(ILogger<UsersController> logger, IUsersAdministration users, IInvitationGenerator invitationGenerator)
{
_logger = logger;
_users = users;
}
[HttpGet]
public async Task<IActionResult> AllUsers()
{
+12 -2
View File
@@ -1,3 +1,4 @@
using Govor.API.Services;
using Govor.API.Services.Authentication;
using Govor.Core.DTOs;
using Govor.API.Services.Authentication.Interfaces;
@@ -9,12 +10,14 @@ namespace Govor.API.Controllers;
[Route("api/[controller]")]
public class AuthController : Controller
{
private IInvitesService _invitesService;
private IAccountService _accountService;
private ILogger<AuthController> _logger;
public AuthController(IAccountService accountService, ILogger<AuthController> logger)
public AuthController(IAccountService accountService, IInvitesService invitesService, ILogger<AuthController> logger)
{
_accountService = accountService;
_invitesService = invitesService;
_logger = logger;
}
@@ -29,7 +32,9 @@ public class AuthController : Controller
return BadRequest(ModelState);
}
var token = await _accountService.RegistrationAsync(registrationDto.Name, registrationDto.Password, registrationDto.InviteLink);
var invite = _invitesService.Validate(registrationDto.InviteLink);
var token = await _accountService.RegistrationAsync(registrationDto.Name, registrationDto.Password, invite);
_logger.LogInformation($"Register request for {registrationDto.Name}");
return Ok(new { token });
}
@@ -38,6 +43,11 @@ public class AuthController : Controller
_logger.LogWarning(ex, $"Registration failed for user {registrationDto.Name}");
return BadRequest("Registration failed: user already exists.");
}
catch (InviteLinkInvalidException ex)
{
_logger.LogWarning(ex, $"Invite link invalid: {registrationDto.InviteLink}");
return BadRequest("Invite link invalid.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during registration for user {Name}", registrationDto.Name);
@@ -25,6 +25,8 @@ public static class ConfigurationProgramExtensions
services.AddScoped<IJwtService, JwtService>();
services.AddScoped<IAccountService, AuthService>();
services.AddScoped<IUsersAdministration, UsersService>();
services.AddScoped<IInvitesService, InvitesService>();
services.AddScoped<IInvitationGenerator, InvitationGenerator>();
}
public static void AddRepositories(this IServiceCollection services)
+1
View File
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
@@ -0,0 +1,6 @@
namespace Govor.API.Services.AdminsStuff.Interfaces;
public interface IInvitationGenerator
{
public Task<string> GenerateInvitationCode(DateTime time, int maxUsers, bool isAdmin, string description = "");
}
@@ -0,0 +1,26 @@
using Govor.API.Services.AdminsStuff.Interfaces;
using Govor.Core.Models;
using Govor.Core.Repositories.Invaites;
namespace Govor.API.Services.AdminsStuff;
public class InvitationGenerator(IInvitesRepository repository) : IInvitationGenerator
{
public async Task<string> GenerateInvitationCode(DateTime time, int maxUsers, bool isAdmin, string description = "")
{
Invitation newInvitation = new Invitation()
{
Id = Guid.NewGuid(),
Description = description,
MaxParticipants = maxUsers,
DateCreated = DateTime.UtcNow,
EndDate = time.ToUniversalTime(),
Code = Guid.NewGuid().ToString("N"),
IsAdmin = isAdmin
};
await repository.AddAsync(newInvitation);
return newInvitation.Code;
}
}
@@ -4,6 +4,7 @@ using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Models;
using Govor.Core.Repositories.Users;
using Govor.API.Services;
using Govor.API.Services.AdminsStuff.Interfaces;
using Govor.Core.Repositories.Admins;
using Govor.Core.Repositories.Invaites;
@@ -15,35 +16,27 @@ 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,
IInvitesRepository invitesRepository,
IAdminsRepository adminsRepository)
IAdminsRepository adminsRepository
)
{
_usersRepository = usersRepository;
_jwtService = jwtService;
_passwordHasher = passwordHasher;
_invitesRepository = invitesRepository;
_adminsRepository = adminsRepository;
}
public async Task<string> RegistrationAsync(string name, string password, string inviteCode)
public async Task<string> RegistrationAsync(string name, string password, Invitation invitation)
{
// 1. Проверка существования имени
if (await _usersRepository.ExistsUsernameAsync(name))
throw new UserAlreadyExistException(name);
// 2. Проверка валидности инвайта
var invite = await _invitesRepository.FindByCodeAsync(inviteCode);
// 3. Генерация пароля
var passwordHash = _passwordHasher.Hash(password);
// 4. Создание пользователя
var user = new User
{
Id = Guid.NewGuid(),
@@ -53,22 +46,13 @@ public class AuthService : IAccountService
CreatedOn = DateOnly.FromDateTime(DateTime.UtcNow),
IconId = Guid.NewGuid(),
WasOnline = DateTime.UtcNow,
InviteId = invite.Id
InviteId = invitation.Id
};
// 5. Добавление пользователя
await _usersRepository.AddAsync(user);
// 6. Назначение роли, если инвайт — админский
if (invite.IsAdmin)
{
await _adminsRepository.AddAsync(new Admin
{
UserId = user.Id
});
}
// 7. Генерация токена
SetRole(user, invitation);
return _jwtService.GenerateJwtToken(user);
}
@@ -85,6 +69,12 @@ public class AuthService : IAccountService
return _jwtService.GenerateJwtToken(user);
}
private async void SetRole(User user, Invitation invitation)
{
if(invitation.IsAdmin)
await _adminsRepository.AddAsync(new Admin() { UserId = user.Id });
}
}
public class LoginUserException : GovorCoreException { }
@@ -4,6 +4,6 @@ namespace Govor.API.Services.Authentication.Interfaces;
public interface IAccountService
{
public Task<string> RegistrationAsync(string name, string password, string inviteCode);
public Task<string> RegistrationAsync(string name, string password, Invitation invitation);
public Task<string> LoginAsync(string name, string password);
}
@@ -0,0 +1,9 @@
using Govor.Core.Models;
namespace Govor.API.Services.Authentication.Interfaces;
public interface IInvitesService
{
public Task<string> GetRole(User user);
public Invitation Validate(string inviteCode);
}
@@ -0,0 +1,52 @@
using Govor.API.Services.Authentication.Interfaces;
using Govor.Core;
using Govor.Core.Models;
using Govor.Core.Repositories.Invaites;
using Govor.Data.Repositories.Exceptions;
namespace Govor.API.Services.Authentication;
public class InvitesService : IInvitesService
{
private readonly IInvitesRepository _invitesRepository;
public InvitesService(IInvitesRepository invitesRepository)
{
_invitesRepository = invitesRepository;
}
public async Task<string> GetRole(User user)
{
try
{
var invitation = await _invitesRepository.FindByIdAsync(user.InviteId);
return invitation.IsAdmin ? "Admin" : "User";
}
catch (NotFoundByKeyException<Guid>)
{
return "User";
}
}
public Invitation Validate(string inviteCode)
{
var invite = _invitesRepository.FindByCodeAsync(inviteCode).Result;
if (invite.EndDate < DateTime.Now ||
invite.MaxParticipants <= invite.Users.Count)
{
invite.IsActive = false;
_invitesRepository.UpdateAsync(invite);
throw new InviteLinkInvalidException(inviteCode);
}
return invite;
}
public string GenerateInvitationLink(Invitation invitation)
{
throw new NotImplementedException();
}
}
public class InviteLinkInvalidException(string inviteCode) : GovorCoreException($"Invite link invalid: {inviteCode}");
@@ -13,21 +13,20 @@ namespace Govor.API.Services.Authentication;
public class JwtService : IJwtService
{
private JwtOption _jwtOption;
private IInvitesRepository _invitesRepository;
private IInvitesService _invitesService;
public JwtService(IOptions<JwtOption> options)
public JwtService(IOptions<JwtOption> options, IInvitesService invitesService)
{
_jwtOption = options.Value;
_invitesService = invitesService;
}
public string GenerateJwtToken(User user)
{
var invite = _invitesRepository.FindByIdAsync(user.InviteId).Result;
var claims = new[]
{
new Claim("userID", user.Id.ToString()),
new Claim(ClaimTypes.Role, invite.IsAdmin ? "Admin" : "User", ClaimValueTypes.String)
new Claim(ClaimTypes.Role, _invitesService.GetRole(user).Result, ClaimValueTypes.String)
};
var singing = new SigningCredentials(
+13
View File
@@ -0,0 +1,13 @@
namespace Govor.Core.DTOs;
public class InvitationDto
{
public Guid Id { get; set; }
public bool IsAdmin { get; set; }
public bool IsActive { get; set; }
public string Code {get; set;}
public DateTime CreatedAt { get; set; }
public DateTime EndAt { get; set; }
public int MaxParticipants { get; set; }
public string Description { get; set; }
}
@@ -15,10 +15,10 @@ public class InvitationValidator : IObjectValidator<Invitation>
throw new ArgumentException("Id cannot be empty", nameof(inv.Id));
if(inv.DateCreated == DateTime.MinValue)
throw new ArgumentException("DateCreated cannot be empty", nameof(inv.DateCreated));
if(inv.EndDate < inv.DateCreated)
throw new ArgumentException("EndDate cannot be less than StartDate", nameof(inv.EndDate));
if(inv.MaxParticipants <= 0)
throw new ArgumentException("MaxParticipants cannot be less than 0", nameof(inv.MaxParticipants));
if(inv.EndDate < inv.DateCreated && inv.IsActive)
throw new ArgumentException("EndDate cannot be less than StartDate when is active", nameof(inv.EndDate));
if((inv.MaxParticipants <= 0 || inv.MaxParticipants <= inv.Users.Count) && inv.IsActive)
throw new ArgumentException("MaxParticipants cannot be less than 0 or users cannot be more then MaxParticipants when is active", nameof(inv.MaxParticipants));
if(inv.Code == string.Empty || inv.Code.Length < MIN_INVITATION_LENGTH)
throw new ArgumentException($"Code cannot be empty or less then {MIN_INVITATION_LENGTH}", nameof(inv.Code));
}
+1
View File
@@ -4,6 +4,7 @@ public class Invitation
{
public Guid Id { get; set; }
public bool IsAdmin { get; set; }
public bool IsActive { get; set; } = true;
public string Code { get; set; }
public string Description { get; set; }
public DateTime DateCreated { get; set; }
@@ -5,5 +5,6 @@ namespace Govor.Core.Repositories.Invaites;
public interface IInvitesExist
{
bool Exist(Invitation invitation);
bool Exist(string code);
bool Exist(Guid guid);
}
@@ -0,0 +1,9 @@
namespace Govor.Core.Requests;
public class CreateInvitationRequest
{
public DateTime EndDate { get; set; }
public int MaxParticipants { get; set; }
public bool IsAdmin { get; set; }
public string Description { get; set; }
}
-129
View File
@@ -1,129 +0,0 @@
// <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("20250616151700_initial")]
partial class initial
{
/// <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.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.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("HashPassword")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("IconId")
.HasColumnType("uuid");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("WasOnline")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
}
}
@@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ChatGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
InviteCode = table.Column<List<string>>(type: "text[]", nullable: false),
IsChannel = table.Column<bool>(type: "boolean", nullable: false),
IsPrivate = table.Column<bool>(type: "boolean", nullable: false),
Admins = table.Column<List<Guid>>(type: "uuid[]", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChatGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GroupAdmins",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
GroupId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GroupAdmins", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GroupMemberships",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
GroupId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
IsBanned = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GroupMemberships", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Username = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: false),
HashPassword = table.Column<string>(type: "text", nullable: false),
IconId = table.Column<Guid>(type: "uuid", nullable: false),
CreatedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
WasOnline = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ChatGroups");
migrationBuilder.DropTable(
name: "GroupAdmins");
migrationBuilder.DropTable(
name: "GroupMemberships");
migrationBuilder.DropTable(
name: "Users");
}
}
}
@@ -1,129 +0,0 @@
// <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("20250618082839_AddPasswordHashColumn")]
partial class AddPasswordHashColumn
{
/// <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.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.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");
});
#pragma warning restore 612, 618
}
}
}
@@ -1,45 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class AddPasswordHashColumn : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "HashPassword",
table: "Users",
newName: "PasswordHash");
migrationBuilder.AlterColumn<DateOnly>(
name: "CreatedOn",
table: "Users",
type: "date",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "PasswordHash",
table: "Users",
newName: "HashPassword");
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
table: "Users",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateOnly),
oldType: "date");
}
}
}
@@ -1,310 +0,0 @@
// <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("20250622044435_MessagesInit")]
partial class MessagesInit
{
/// <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.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.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
}
}
}
@@ -1,331 +0,0 @@
// <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
}
}
}
@@ -1,39 +0,0 @@
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
@@ -1,375 +0,0 @@
// <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
}
}
}
@@ -1,69 +0,0 @@
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");
}
}
}
@@ -1,22 +0,0 @@
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)
{
}
}
}
@@ -1,22 +0,0 @@
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)
{
}
}
}
@@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Govor.Data.Migrations
{
[DbContext(typeof(GovorDbContext))]
[Migration("20250624072548_InitialCreate")]
[Migration("20250624133431_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@@ -108,9 +108,17 @@ namespace Govor.Data.Migrations
.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");
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
@@ -6,11 +7,71 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class MessagesInit : Migration
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ChatGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
InviteCode = table.Column<List<string>>(type: "text[]", nullable: false),
IsChannel = table.Column<bool>(type: "boolean", nullable: false),
IsPrivate = table.Column<bool>(type: "boolean", nullable: false),
Admins = table.Column<List<Guid>>(type: "uuid[]", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChatGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GroupAdmins",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
GroupId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GroupAdmins", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GroupMemberships",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
GroupId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
IsBanned = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GroupMemberships", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Invitations",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
IsAdmin = table.Column<bool>(type: "boolean", nullable: false),
Code = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", 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.CreateTable(
name: "Messages",
columns: table => new
@@ -36,6 +97,30 @@ namespace Govor.Data.Migrations
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Username = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: false),
IconId = table.Column<Guid>(type: "uuid", nullable: false),
CreatedOn = table.Column<DateOnly>(type: "date", nullable: false),
WasOnline = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
InviteId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.ForeignKey(
name: "FK_Users_Invitations_InviteId",
column: x => x.InviteId,
principalTable: "Invitations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "MediaAttachments",
columns: table => new
@@ -58,6 +143,43 @@ namespace Govor.Data.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "MessageViews",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ViewedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MessageViews", x => x.Id);
table.ForeignKey(
name: "FK_MessageViews_Messages_MessageId",
column: x => x.MessageId,
principalTable: "Messages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
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);
});
migrationBuilder.CreateTable(
name: "MessageReactions",
columns: table => new
@@ -85,26 +207,6 @@ namespace Govor.Data.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "MessageViews",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ViewedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MessageViews", x => x.Id);
table.ForeignKey(
name: "FK_MessageViews_Messages_MessageId",
column: x => x.MessageId,
principalTable: "Messages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_MediaAttachments_MessageId",
table: "MediaAttachments",
@@ -131,11 +233,28 @@ namespace Govor.Data.Migrations
table: "MessageViews",
columns: new[] { "MessageId", "UserId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Users_InviteId",
table: "Users",
column: "InviteId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Admins");
migrationBuilder.DropTable(
name: "ChatGroups");
migrationBuilder.DropTable(
name: "GroupAdmins");
migrationBuilder.DropTable(
name: "GroupMemberships");
migrationBuilder.DropTable(
name: "MediaAttachments");
@@ -145,8 +264,14 @@ namespace Govor.Data.Migrations
migrationBuilder.DropTable(
name: "MessageViews");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Messages");
migrationBuilder.DropTable(
name: "Invitations");
}
}
}
@@ -13,8 +13,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Govor.Data.Migrations
{
[DbContext(typeof(GovorDbContext))]
[Migration("20250624072330_FixinviteAdd")]
partial class FixinviteAdd
[Migration("20250624141242_inviteAddIsActive")]
partial class inviteAddIsActive
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -108,12 +108,23 @@ namespace Govor.Data.Migrations
.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");
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class inviteAddIsActive : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsActive",
table: "Invitations",
type: "boolean",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsActive",
table: "Invitations");
}
}
}
@@ -105,12 +105,23 @@ namespace Govor.Data.Migrations
.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");
@@ -1,4 +1,10 @@
namespace Govor.Data.Repositories.Exceptions;
public class AdditionException(string s, Exception ex)
: Exception(s, ex);
public class AdditionException : Exception
{
public AdditionException(string s, Exception ex) : base(s, ex)
{
}
public AdditionException(string s) : base(s) { }
}
@@ -59,6 +59,9 @@ public class InvitesRepository : IInvitesRepository
{
_validator.Validate(invitation);
if(Exist(invitation.Code))
throw new AdditionException("Invitation with given code already exists");
_context.Invitations.Add(invitation);
await _context.SaveChangesAsync();
}
@@ -136,6 +139,11 @@ public class InvitesRepository : IInvitesRepository
);
}
public bool Exist(string code)
{
return _context.Invitations.Any(i => i.Code == code);
}
public bool Exist(Guid guid)
{
return _context.Invitations.Any(i => i.Id == guid);