From 0d9a42dd24e7d1906ff664ee894dc9a92d4cee1a Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 22 Mar 2024 02:17:42 +0100 Subject: [PATCH 1/5] swagger added --- WatchIt/Program.cs | 50 ++++++++++++++++++++++++---- WatchIt/WatchIt.csproj | 8 ++--- WatchIt/WebAPI/AccountsController.cs | 16 +++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 WatchIt/WebAPI/AccountsController.cs diff --git a/WatchIt/Program.cs b/WatchIt/Program.cs index 474574c..3086beb 100644 --- a/WatchIt/Program.cs +++ b/WatchIt/Program.cs @@ -20,12 +20,9 @@ namespace WatchIt { _builder = WebApplication.CreateBuilder(args); - // Logging - _builder.Logging.ClearProviders(); - _builder.Logging.AddConsole(); - - // Database - _builder.Services.AddDbContext(x => x.UseNpgsql(_builder.Configuration.GetConnectionString("Default")), ServiceLifetime.Singleton); + ConfigureLogging(); + ConfigureDatabase(); + ConfigureWebAPI(); // Add services to the container. _builder.Services.AddRazorComponents() @@ -40,12 +37,29 @@ namespace WatchIt // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } + else + { + app.UseSwagger(x => + { + x.RouteTemplate = "api/swagger/{documentname}/swagger.json"; + }); + app.UseSwaggerUI(x => + { + x.SwaggerEndpoint("/api/swagger/v1/swagger.json", "WatchIt API"); + x.RoutePrefix = "api/swagger"; + }); + } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAntiforgery(); + app.UseAuthentication(); + app.UseAuthorization(); + + app.MapControllers(); + app.MapRazorComponents() .AddInteractiveServerRenderMode(); @@ -53,5 +67,29 @@ namespace WatchIt } #endregion + + + + #region PRIVATE METHODS + + protected static void ConfigureLogging() + { + _builder.Logging.ClearProviders(); + _builder.Logging.AddConsole(); + } + + protected static void ConfigureDatabase() + { + _builder.Services.AddDbContext(x => x.UseNpgsql(_builder.Configuration.GetConnectionString("Default")), ServiceLifetime.Singleton); + } + + protected static void ConfigureWebAPI() + { + _builder.Services.AddControllers(); + _builder.Services.AddEndpointsApiExplorer(); + _builder.Services.AddSwaggerGen(); + } + + #endregion } } diff --git a/WatchIt/WatchIt.csproj b/WatchIt/WatchIt.csproj index df189f9..8207f5d 100644 --- a/WatchIt/WatchIt.csproj +++ b/WatchIt/WatchIt.csproj @@ -6,10 +6,6 @@ enable - - - - @@ -23,6 +19,10 @@ + + + + diff --git a/WatchIt/WebAPI/AccountsController.cs b/WatchIt/WebAPI/AccountsController.cs new file mode 100644 index 0000000..c897fdf --- /dev/null +++ b/WatchIt/WebAPI/AccountsController.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI +{ + [ApiController] + [Route("api/accounts")] + public class AccountsController : ControllerBase + { + [HttpPost] + [Route("create-account")] + public async Task CreateAccount() + { + } + } +} From fcca2119a589091da3a72de573fbc771469f39ce Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 28 Mar 2024 19:17:46 +0100 Subject: [PATCH 2/5] auth changes --- .../WatchIt.Database.Model/Account/Account.cs | 15 +- .../Account/AccountRefreshToken.cs | 57 + .../WatchIt.Database/DatabaseContext.cs | 2 + ...rNotRequiredAndDefaultNotAdmin.Designer.cs | 1297 ++++++++++++++++ ...003_GenderNotRequiredAndDefaultNotAdmin.cs | 77 + ...004_AccountDescriptionNullable.Designer.cs | 1296 ++++++++++++++++ ...4144605_0004_AccountDescriptionNullable.cs | 40 + ...0005_AccountRefreshTokensAdded.Designer.cs | 1331 ++++++++++++++++ ...24195235_0005_AccountRefreshTokensAdded.cs | 52 + ...006_AccountRefreshTokenChanges.Designer.cs | 1334 +++++++++++++++++ ...4205952_0006_AccountRefreshTokenChanges.cs | 39 + .../DatabaseContextModelSnapshot.cs | 49 +- .../Authenticate/AuthenticateRequest.cs | 26 + .../Authenticate/AuthenticateResponse.cs | 22 + .../Accounts/Register/RegisterRequest.cs | 27 + .../Accounts/Register/RegisterResponse.cs | 44 + .../WatchIt.Shared.Models/RequestResult.cs | 107 ++ .../RequestResultStatus.cs | 20 + .../WatchIt.Shared.Models.csproj | 18 + .../AccountsController.cs | 47 + .../WatchIt.WebAPI.Controllers.csproj | 18 + .../AccountsControllerService.cs | 88 ++ ...WatchIt.WebAPI.Services.Controllers.csproj | 20 + .../ConfigurationService.cs | 26 + .../Models/AccessToken.cs | 13 + .../Models/Authentication.cs | 16 + .../Models/ConfigurationData.cs | 13 + .../Models/RefreshToken.cs | 14 + ...bAPI.Services.Utility.Configuration.csproj | 13 + .../JWTService.cs | 131 ++ .../TokenNotExtendableException.cs | 13 + .../TokenNotFoundException.cs | 13 + ...WatchIt.WebAPI.Services.Utility.JWT.csproj | 21 + .../UserService.cs | 18 + ...atchIt.WebAPI.Services.Utility.User.csproj | 13 + .../Accounts/AuthenticateRequestValidator.cs | 26 + .../Accounts/RegisterRequestValidator.cs | 31 + .../CustomValidators.cs | 18 + .../WatchIt.WebAPI.Validators.csproj | 19 + WatchIt.sln | 59 +- WatchIt/Program.cs | 93 +- WatchIt/WatchIt.csproj | 13 + WatchIt/WebAPI/AccountsController.cs | 16 - WatchIt/appsettings.json | 20 +- genre.json | 18 - 45 files changed, 6588 insertions(+), 55 deletions(-) create mode 100644 WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs create mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs create mode 100644 WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj delete mode 100644 WatchIt/WebAPI/AccountsController.cs delete mode 100644 genre.json diff --git a/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs b/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs index 57ac65f..abf2064 100644 --- a/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs +++ b/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs @@ -22,8 +22,8 @@ namespace WatchIt.Database.Model.Account public long Id { get; set; } public string Username { get; set; } public string Email { get; set; } - public string Description { get; set; } - public short GenderId { get; set; } + public string? Description { get; set; } + public short? GenderId { get; set; } public Guid? ProfilePictureId { get; set; } public Guid? BackgroundPictureId { get; set; } public byte[] Password { get; set; } @@ -49,6 +49,8 @@ namespace WatchIt.Database.Model.Account public IEnumerable RatingMediaSeriesSeason { get; set; } public IEnumerable RatingMediaSeriesEpisode { get; set; } + public IEnumerable AccountRefreshTokens { get; set; } + #endregion @@ -76,10 +78,8 @@ namespace WatchIt.Database.Model.Account builder.HasOne(x => x.Gender) .WithMany() - .HasForeignKey(x => x.GenderId) - .IsRequired(); - builder.Property(x => x.GenderId) - .IsRequired(); + .HasForeignKey(x => x.GenderId); + builder.Property(x => x.GenderId); builder.HasOne(x => x.ProfilePicture) .WithOne(x => x.Account) @@ -104,7 +104,8 @@ namespace WatchIt.Database.Model.Account .IsRequired(); builder.Property(x => x.IsAdmin) - .IsRequired(); + .IsRequired() + .HasDefaultValue(false); builder.Property(x => x.CreationDate) .IsRequired() diff --git a/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs b/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs new file mode 100644 index 0000000..92aac22 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.Database.Model.Account +{ + public class AccountRefreshToken : IEntity + { + #region PROPERTIES + + public Guid Id { get; set; } + public long AccountId { get; set; } + public DateTime ExpirationDate { get; set; } + public bool IsExtendable { get; set; } + + #endregion + + + + #region NAVIGATION + + public Account Account { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + static void IEntity.Build(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.AccountRefreshTokens) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.ExpirationDate) + .IsRequired(); + + builder.Property(x => x.IsExtendable) + .IsRequired(); + } + + #endregion + } +} diff --git a/WatchIt.Database/WatchIt.Database/DatabaseContext.cs b/WatchIt.Database/WatchIt.Database/DatabaseContext.cs index b519f43..da01176 100644 --- a/WatchIt.Database/WatchIt.Database/DatabaseContext.cs +++ b/WatchIt.Database/WatchIt.Database/DatabaseContext.cs @@ -37,6 +37,7 @@ namespace WatchIt.Database // Account public virtual DbSet Accounts { get; set; } public virtual DbSet AccountProfilePictures { get; set; } + public virtual DbSet AccountRefreshTokens { get; set; } // Media public virtual DbSet Media { get; set; } @@ -87,6 +88,7 @@ namespace WatchIt.Database // Account EntityBuilder.Build(modelBuilder); EntityBuilder.Build(modelBuilder); + EntityBuilder.Build(modelBuilder); // Media EntityBuilder.Build(modelBuilder); diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs new file mode 100644 index 0000000..e4bfd8b --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs @@ -0,0 +1,1297 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WatchIt.Database; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin")] + partial class _0003_GenderNotRequiredAndDefaultNotAdmin + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundPictureId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("IsAdmin") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("LeftSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("bytea"); + + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + + b.Property("RightSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("BackgroundPictureId"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("ProfilePictureId") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountProfilePictures"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Countries"); + + b.HasData( + new + { + Id = (short)1, + Name = "Afghanistan" + }, + new + { + Id = (short)2, + Name = "Albania" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genders"); + + b.HasData( + new + { + Id = (short)1, + Name = "Male" + }, + new + { + Id = (short)2, + Name = "Female" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genres"); + + b.HasData( + new + { + Id = (short)1, + Name = "Comedy" + }, + new + { + Id = (short)2, + Name = "Thriller" + }, + new + { + Id = (short)3, + Name = "Horror" + }, + new + { + Id = (short)4, + Name = "Action" + }, + new + { + Id = (short)5, + Name = "Drama" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Length") + .HasColumnType("interval"); + + b.Property("MediaPosterImageId") + .HasColumnType("uuid"); + + b.Property("OriginalTitle") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaPosterImageId") + .IsUnique(); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.Property("GenreId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("GenreId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Budget") + .HasColumnType("money"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaMovies"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("IsMediaBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUniversalBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("MediaPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaPosterImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.Property("CountryId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("CountryId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaProductionCountrys"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HasEnded") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsSpecial") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("MediaSeriesEpisodes"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaSeriesId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesId"); + + b.ToTable("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("DeathDate") + .HasColumnType("date"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("FullName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PersonPhotoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonPhotoId") + .IsUnique(); + + b.ToTable("Persons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonActorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonActorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonActorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Actor" + }, + new + { + Id = (short)2, + Name = "Supporting actor" + }, + new + { + Id = (short)3, + Name = "Voice actor" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonCreatorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonCreatorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonCreatorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Director" + }, + new + { + Id = (short)2, + Name = "Producer" + }, + new + { + Id = (short)3, + Name = "Screenwriter" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("RatingsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesEpisodeId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesEpisodeId"); + + b.ToTable("RatingsMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("RatingsMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonActorRoleId"); + + b.ToTable("RatingsPersonActorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonCreatorRoleId"); + + b.ToTable("RatingsPersonCreatorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("ViewCountsMedia", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonId"); + + b.ToTable("ViewCountsPerson", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") + .WithMany() + .HasForeignKey("BackgroundPictureId"); + + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId"); + + b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") + .WithOne("Account") + .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); + + b.Navigation("BackgroundPicture"); + + b.Navigation("Gender"); + + b.Navigation("ProfilePicture"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); + + b.Navigation("MediaPosterImage"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") + .WithMany("MediaGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaGenres") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Genre"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaPhotoImages") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.HasOne("WatchIt.Database.Model.Common.Country", "Country") + .WithMany("MediaProductionCountries") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaProductionCountries") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("MediaSeriesEpisodes") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") + .WithMany("MediaSeriesSeasons") + .HasForeignKey("MediaSeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") + .WithOne("Person") + .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); + + b.Navigation("Gender"); + + b.Navigation("PersonPhoto"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonActorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") + .WithMany() + .HasForeignKey("PersonActorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonActorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonActorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonCreatorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") + .WithMany() + .HasForeignKey("PersonCreatorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonCreatorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonCreatorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMedia") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("RatingMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("MediaSeriesEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonActorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") + .WithMany("RatingPersonActorRole") + .HasForeignKey("PersonActorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("PersonCreatorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("ViewCountsMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("ViewCountsPerson") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Navigation("RatingMedia"); + + b.Navigation("RatingMediaSeriesEpisode"); + + b.Navigation("RatingMediaSeriesSeason"); + + b.Navigation("RatingPersonActorRole"); + + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Navigation("Account") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Navigation("MediaProductionCountries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Navigation("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Navigation("MediaGenres"); + + b.Navigation("MediaPhotoImages"); + + b.Navigation("MediaProductionCountries"); + + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("RatingMedia"); + + b.Navigation("ViewCountsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Navigation("Media") + .IsRequired(); + + b.Navigation("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Navigation("RatingMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Navigation("MediaSeriesEpisodes"); + + b.Navigation("RatingMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("ViewCountsPerson"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Navigation("RatingPersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Navigation("Person") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs new file mode 100644 index 0000000..9c843f9 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + /// + public partial class _0003_GenderNotRequiredAndDefaultNotAdmin : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Accounts_Genders_GenderId", + table: "Accounts"); + + migrationBuilder.AlterColumn( + name: "IsAdmin", + table: "Accounts", + type: "boolean", + nullable: false, + defaultValue: false, + oldClrType: typeof(bool), + oldType: "boolean"); + + migrationBuilder.AlterColumn( + name: "GenderId", + table: "Accounts", + type: "smallint", + nullable: true, + oldClrType: typeof(short), + oldType: "smallint"); + + migrationBuilder.AddForeignKey( + name: "FK_Accounts_Genders_GenderId", + table: "Accounts", + column: "GenderId", + principalTable: "Genders", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Accounts_Genders_GenderId", + table: "Accounts"); + + migrationBuilder.AlterColumn( + name: "IsAdmin", + table: "Accounts", + type: "boolean", + nullable: false, + oldClrType: typeof(bool), + oldType: "boolean", + oldDefaultValue: false); + + migrationBuilder.AlterColumn( + name: "GenderId", + table: "Accounts", + type: "smallint", + nullable: false, + defaultValue: (short)0, + oldClrType: typeof(short), + oldType: "smallint", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Accounts_Genders_GenderId", + table: "Accounts", + column: "GenderId", + principalTable: "Genders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs new file mode 100644 index 0000000..77f4446 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs @@ -0,0 +1,1296 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WatchIt.Database; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240324144605_0004_AccountDescriptionNullable")] + partial class _0004_AccountDescriptionNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundPictureId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("IsAdmin") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("LeftSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("bytea"); + + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + + b.Property("RightSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("BackgroundPictureId"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("ProfilePictureId") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountProfilePictures"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Countries"); + + b.HasData( + new + { + Id = (short)1, + Name = "Afghanistan" + }, + new + { + Id = (short)2, + Name = "Albania" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genders"); + + b.HasData( + new + { + Id = (short)1, + Name = "Male" + }, + new + { + Id = (short)2, + Name = "Female" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genres"); + + b.HasData( + new + { + Id = (short)1, + Name = "Comedy" + }, + new + { + Id = (short)2, + Name = "Thriller" + }, + new + { + Id = (short)3, + Name = "Horror" + }, + new + { + Id = (short)4, + Name = "Action" + }, + new + { + Id = (short)5, + Name = "Drama" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Length") + .HasColumnType("interval"); + + b.Property("MediaPosterImageId") + .HasColumnType("uuid"); + + b.Property("OriginalTitle") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaPosterImageId") + .IsUnique(); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.Property("GenreId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("GenreId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Budget") + .HasColumnType("money"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaMovies"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("IsMediaBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUniversalBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("MediaPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaPosterImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.Property("CountryId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("CountryId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaProductionCountrys"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HasEnded") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsSpecial") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("MediaSeriesEpisodes"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaSeriesId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesId"); + + b.ToTable("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("DeathDate") + .HasColumnType("date"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("FullName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PersonPhotoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonPhotoId") + .IsUnique(); + + b.ToTable("Persons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonActorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonActorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonActorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Actor" + }, + new + { + Id = (short)2, + Name = "Supporting actor" + }, + new + { + Id = (short)3, + Name = "Voice actor" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonCreatorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonCreatorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonCreatorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Director" + }, + new + { + Id = (short)2, + Name = "Producer" + }, + new + { + Id = (short)3, + Name = "Screenwriter" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("RatingsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesEpisodeId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesEpisodeId"); + + b.ToTable("RatingsMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("RatingsMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonActorRoleId"); + + b.ToTable("RatingsPersonActorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonCreatorRoleId"); + + b.ToTable("RatingsPersonCreatorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("ViewCountsMedia", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonId"); + + b.ToTable("ViewCountsPerson", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") + .WithMany() + .HasForeignKey("BackgroundPictureId"); + + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId"); + + b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") + .WithOne("Account") + .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); + + b.Navigation("BackgroundPicture"); + + b.Navigation("Gender"); + + b.Navigation("ProfilePicture"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); + + b.Navigation("MediaPosterImage"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") + .WithMany("MediaGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaGenres") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Genre"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaPhotoImages") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.HasOne("WatchIt.Database.Model.Common.Country", "Country") + .WithMany("MediaProductionCountries") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaProductionCountries") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("MediaSeriesEpisodes") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") + .WithMany("MediaSeriesSeasons") + .HasForeignKey("MediaSeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") + .WithOne("Person") + .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); + + b.Navigation("Gender"); + + b.Navigation("PersonPhoto"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonActorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") + .WithMany() + .HasForeignKey("PersonActorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonActorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonActorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonCreatorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") + .WithMany() + .HasForeignKey("PersonCreatorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonCreatorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonCreatorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMedia") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("RatingMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("MediaSeriesEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonActorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") + .WithMany("RatingPersonActorRole") + .HasForeignKey("PersonActorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("PersonCreatorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("ViewCountsMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("ViewCountsPerson") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Navigation("RatingMedia"); + + b.Navigation("RatingMediaSeriesEpisode"); + + b.Navigation("RatingMediaSeriesSeason"); + + b.Navigation("RatingPersonActorRole"); + + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Navigation("Account") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Navigation("MediaProductionCountries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Navigation("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Navigation("MediaGenres"); + + b.Navigation("MediaPhotoImages"); + + b.Navigation("MediaProductionCountries"); + + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("RatingMedia"); + + b.Navigation("ViewCountsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Navigation("Media") + .IsRequired(); + + b.Navigation("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Navigation("RatingMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Navigation("MediaSeriesEpisodes"); + + b.Navigation("RatingMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("ViewCountsPerson"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Navigation("RatingPersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Navigation("Person") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs new file mode 100644 index 0000000..db5b153 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + /// + public partial class _0004_AccountDescriptionNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Description", + table: "Accounts", + type: "character varying(1000)", + maxLength: 1000, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(1000)", + oldMaxLength: 1000); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Description", + table: "Accounts", + type: "character varying(1000)", + maxLength: 1000, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "character varying(1000)", + oldMaxLength: 1000, + oldNullable: true); + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs new file mode 100644 index 0000000..eadf7d4 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs @@ -0,0 +1,1331 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WatchIt.Database; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240324195235_0005_AccountRefreshTokensAdded")] + partial class _0005_AccountRefreshTokensAdded + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundPictureId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("IsAdmin") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("LeftSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("bytea"); + + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + + b.Property("RightSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("BackgroundPictureId"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("ProfilePictureId") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountProfilePictures"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("Lifetime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountRefreshTokens"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Countries"); + + b.HasData( + new + { + Id = (short)1, + Name = "Afghanistan" + }, + new + { + Id = (short)2, + Name = "Albania" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genders"); + + b.HasData( + new + { + Id = (short)1, + Name = "Male" + }, + new + { + Id = (short)2, + Name = "Female" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genres"); + + b.HasData( + new + { + Id = (short)1, + Name = "Comedy" + }, + new + { + Id = (short)2, + Name = "Thriller" + }, + new + { + Id = (short)3, + Name = "Horror" + }, + new + { + Id = (short)4, + Name = "Action" + }, + new + { + Id = (short)5, + Name = "Drama" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Length") + .HasColumnType("interval"); + + b.Property("MediaPosterImageId") + .HasColumnType("uuid"); + + b.Property("OriginalTitle") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaPosterImageId") + .IsUnique(); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.Property("GenreId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("GenreId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Budget") + .HasColumnType("money"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaMovies"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("IsMediaBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUniversalBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("MediaPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaPosterImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.Property("CountryId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("CountryId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaProductionCountrys"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HasEnded") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsSpecial") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("MediaSeriesEpisodes"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaSeriesId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesId"); + + b.ToTable("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("DeathDate") + .HasColumnType("date"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("FullName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PersonPhotoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonPhotoId") + .IsUnique(); + + b.ToTable("Persons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonActorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonActorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonActorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Actor" + }, + new + { + Id = (short)2, + Name = "Supporting actor" + }, + new + { + Id = (short)3, + Name = "Voice actor" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonCreatorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonCreatorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonCreatorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Director" + }, + new + { + Id = (short)2, + Name = "Producer" + }, + new + { + Id = (short)3, + Name = "Screenwriter" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("RatingsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesEpisodeId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesEpisodeId"); + + b.ToTable("RatingsMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("RatingsMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonActorRoleId"); + + b.ToTable("RatingsPersonActorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonCreatorRoleId"); + + b.ToTable("RatingsPersonCreatorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("ViewCountsMedia", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonId"); + + b.ToTable("ViewCountsPerson", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") + .WithMany() + .HasForeignKey("BackgroundPictureId"); + + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId"); + + b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") + .WithOne("Account") + .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); + + b.Navigation("BackgroundPicture"); + + b.Navigation("Gender"); + + b.Navigation("ProfilePicture"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("AccountRefreshTokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); + + b.Navigation("MediaPosterImage"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") + .WithMany("MediaGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaGenres") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Genre"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaPhotoImages") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.HasOne("WatchIt.Database.Model.Common.Country", "Country") + .WithMany("MediaProductionCountries") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaProductionCountries") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("MediaSeriesEpisodes") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") + .WithMany("MediaSeriesSeasons") + .HasForeignKey("MediaSeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") + .WithOne("Person") + .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); + + b.Navigation("Gender"); + + b.Navigation("PersonPhoto"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonActorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") + .WithMany() + .HasForeignKey("PersonActorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonActorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonActorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonCreatorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") + .WithMany() + .HasForeignKey("PersonCreatorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonCreatorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonCreatorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMedia") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("RatingMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("MediaSeriesEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonActorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") + .WithMany("RatingPersonActorRole") + .HasForeignKey("PersonActorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("PersonCreatorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("ViewCountsMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("ViewCountsPerson") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Navigation("AccountRefreshTokens"); + + b.Navigation("RatingMedia"); + + b.Navigation("RatingMediaSeriesEpisode"); + + b.Navigation("RatingMediaSeriesSeason"); + + b.Navigation("RatingPersonActorRole"); + + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Navigation("Account") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Navigation("MediaProductionCountries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Navigation("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Navigation("MediaGenres"); + + b.Navigation("MediaPhotoImages"); + + b.Navigation("MediaProductionCountries"); + + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("RatingMedia"); + + b.Navigation("ViewCountsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Navigation("Media") + .IsRequired(); + + b.Navigation("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Navigation("RatingMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Navigation("MediaSeriesEpisodes"); + + b.Navigation("RatingMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("ViewCountsPerson"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Navigation("RatingPersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Navigation("Person") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs new file mode 100644 index 0000000..7e5f5cf --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + /// + public partial class _0005_AccountRefreshTokensAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AccountRefreshTokens", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + AccountId = table.Column(type: "bigint", nullable: false), + Lifetime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountRefreshTokens", x => x.Id); + table.ForeignKey( + name: "FK_AccountRefreshTokens_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AccountRefreshTokens_AccountId", + table: "AccountRefreshTokens", + column: "AccountId"); + + migrationBuilder.CreateIndex( + name: "IX_AccountRefreshTokens_Id", + table: "AccountRefreshTokens", + column: "Id", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AccountRefreshTokens"); + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs new file mode 100644 index 0000000..df17bdb --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs @@ -0,0 +1,1334 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WatchIt.Database; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240324205952_0006_AccountRefreshTokenChanges")] + partial class _0006_AccountRefreshTokenChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundPictureId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("IsAdmin") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("LeftSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("bytea"); + + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + + b.Property("RightSalt") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("BackgroundPictureId"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("ProfilePictureId") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountProfilePictures"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsExtendable") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountRefreshTokens"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Countries"); + + b.HasData( + new + { + Id = (short)1, + Name = "Afghanistan" + }, + new + { + Id = (short)2, + Name = "Albania" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genders"); + + b.HasData( + new + { + Id = (short)1, + Name = "Male" + }, + new + { + Id = (short)2, + Name = "Female" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Genres"); + + b.HasData( + new + { + Id = (short)1, + Name = "Comedy" + }, + new + { + Id = (short)2, + Name = "Thriller" + }, + new + { + Id = (short)3, + Name = "Horror" + }, + new + { + Id = (short)4, + Name = "Action" + }, + new + { + Id = (short)5, + Name = "Drama" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Length") + .HasColumnType("interval"); + + b.Property("MediaPosterImageId") + .HasColumnType("uuid"); + + b.Property("OriginalTitle") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaPosterImageId") + .IsUnique(); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.Property("GenreId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("GenreId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Budget") + .HasColumnType("money"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaMovies"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("IsMediaBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUniversalBackground") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("MediaPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaPosterImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.Property("CountryId") + .HasColumnType("smallint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.HasKey("CountryId", "MediaId"); + + b.HasIndex("MediaId"); + + b.ToTable("MediaProductionCountrys"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HasEnded") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsSpecial") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("MediaSeriesEpisodes"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaSeriesId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesId"); + + b.ToTable("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("DeathDate") + .HasColumnType("date"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("FullName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("GenderId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PersonPhotoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GenderId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonPhotoId") + .IsUnique(); + + b.ToTable("Persons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonActorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonActorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonActorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Actor" + }, + new + { + Id = (short)2, + Name = "Supporting actor" + }, + new + { + Id = (short)3, + Name = "Voice actor" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleTypeId") + .HasColumnType("smallint"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.HasIndex("PersonCreatorRoleTypeId"); + + b.HasIndex("PersonId"); + + b.ToTable("PersonCreatorRoles"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonCreatorRoleTypes"); + + b.HasData( + new + { + Id = (short)1, + Name = "Director" + }, + new + { + Id = (short)2, + Name = "Producer" + }, + new + { + Id = (short)3, + Name = "Screenwriter" + }); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Image") + .IsRequired() + .HasMaxLength(-1) + .HasColumnType("bytea"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UploadDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("PersonPhotoImages"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("RatingsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesEpisodeId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesEpisodeId"); + + b.ToTable("RatingsMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("MediaSeriesSeasonId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaSeriesSeasonId"); + + b.ToTable("RatingsMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonActorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonActorRoleId"); + + b.ToTable("RatingsPersonActorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("PersonCreatorRoleId") + .HasColumnType("uuid"); + + b.Property("Rating") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonCreatorRoleId"); + + b.ToTable("RatingsPersonCreatorRole", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("MediaId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MediaId"); + + b.ToTable("ViewCountsMedia", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .ValueGeneratedOnAdd() + .HasColumnType("date") + .HasDefaultValueSql("now()"); + + b.Property("PersonId") + .HasColumnType("bigint"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("PersonId"); + + b.ToTable("ViewCountsPerson", (string)null); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") + .WithMany() + .HasForeignKey("BackgroundPictureId"); + + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId"); + + b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") + .WithOne("Account") + .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); + + b.Navigation("BackgroundPicture"); + + b.Navigation("Gender"); + + b.Navigation("ProfilePicture"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("AccountRefreshTokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") + .WithOne("Media") + .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); + + b.Navigation("MediaPosterImage"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => + { + b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") + .WithMany("MediaGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaGenres") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Genre"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaPhotoImages") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => + { + b.HasOne("WatchIt.Database.Model.Common.Country", "Country") + .WithMany("MediaProductionCountries") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("MediaProductionCountries") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("MediaSeriesEpisodes") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") + .WithMany("MediaSeriesSeasons") + .HasForeignKey("MediaSeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSeries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") + .WithMany() + .HasForeignKey("GenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") + .WithOne("Person") + .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); + + b.Navigation("Gender"); + + b.Navigation("PersonPhoto"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonActorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") + .WithMany() + .HasForeignKey("PersonActorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonActorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonActorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("PersonCreatorRoles") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") + .WithMany() + .HasForeignKey("PersonCreatorRoleTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("PersonCreatorRoles") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + + b.Navigation("Person"); + + b.Navigation("PersonCreatorRoleType"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMedia") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("RatingMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") + .WithMany("RatingMediaSeriesEpisode") + .HasForeignKey("MediaSeriesEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") + .WithMany("RatingMediaSeriesSeason") + .HasForeignKey("MediaSeriesSeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("MediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonActorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") + .WithMany("RatingPersonActorRole") + .HasForeignKey("PersonActorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") + .WithMany("RatingPersonCreatorRole") + .HasForeignKey("PersonCreatorRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("PersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithMany("ViewCountsMedia") + .HasForeignKey("MediaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => + { + b.HasOne("WatchIt.Database.Model.Person.Person", "Person") + .WithMany("ViewCountsPerson") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => + { + b.Navigation("AccountRefreshTokens"); + + b.Navigation("RatingMedia"); + + b.Navigation("RatingMediaSeriesEpisode"); + + b.Navigation("RatingMediaSeriesSeason"); + + b.Navigation("RatingPersonActorRole"); + + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => + { + b.Navigation("Account") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => + { + b.Navigation("MediaProductionCountries"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => + { + b.Navigation("MediaGenres"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => + { + b.Navigation("MediaGenres"); + + b.Navigation("MediaPhotoImages"); + + b.Navigation("MediaProductionCountries"); + + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("RatingMedia"); + + b.Navigation("ViewCountsMedia"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => + { + b.Navigation("Media") + .IsRequired(); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.Navigation("Media") + .IsRequired(); + + b.Navigation("MediaSeriesSeasons"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => + { + b.Navigation("RatingMediaSeriesEpisode"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => + { + b.Navigation("MediaSeriesEpisodes"); + + b.Navigation("RatingMediaSeriesSeason"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => + { + b.Navigation("PersonActorRoles"); + + b.Navigation("PersonCreatorRoles"); + + b.Navigation("ViewCountsPerson"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => + { + b.Navigation("RatingPersonActorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => + { + b.Navigation("RatingPersonCreatorRole"); + }); + + modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => + { + b.Navigation("Person") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs new file mode 100644 index 0000000..c02ef15 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WatchIt.Database.Migrations +{ + /// + public partial class _0006_AccountRefreshTokenChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Lifetime", + table: "AccountRefreshTokens", + newName: "ExpirationDate"); + + migrationBuilder.AddColumn( + name: "IsExtendable", + table: "AccountRefreshTokens", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsExtendable", + table: "AccountRefreshTokens"); + + migrationBuilder.RenameColumn( + name: "ExpirationDate", + table: "AccountRefreshTokens", + newName: "Lifetime"); + } + } +} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs b/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs index 73b70d5..186e9cc 100644 --- a/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs +++ b/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs @@ -39,7 +39,6 @@ namespace WatchIt.Database.Migrations .HasDefaultValueSql("now()"); b.Property("Description") - .IsRequired() .HasMaxLength(1000) .HasColumnType("character varying(1000)"); @@ -48,11 +47,13 @@ namespace WatchIt.Database.Migrations .HasMaxLength(320) .HasColumnType("character varying(320)"); - b.Property("GenderId") + b.Property("GenderId") .HasColumnType("smallint"); b.Property("IsAdmin") - .HasColumnType("boolean"); + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); b.Property("LastActive") .ValueGeneratedOnAdd() @@ -126,6 +127,31 @@ namespace WatchIt.Database.Migrations b.ToTable("AccountProfilePictures"); }); + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsExtendable") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("AccountRefreshTokens"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => { b.Property("Id") @@ -891,9 +917,7 @@ namespace WatchIt.Database.Migrations b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("GenderId"); b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") .WithOne("Account") @@ -906,6 +930,17 @@ namespace WatchIt.Database.Migrations b.Navigation("ProfilePicture"); }); + modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => + { + b.HasOne("WatchIt.Database.Model.Account.Account", "Account") + .WithMany("AccountRefreshTokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => { b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) @@ -1188,6 +1223,8 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => { + b.Navigation("AccountRefreshTokens"); + b.Navigation("RatingMedia"); b.Navigation("RatingMediaSeriesEpisode"); diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs new file mode 100644 index 0000000..583f8d2 --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace WatchIt.Shared.Models.Accounts.Authenticate +{ + public class AuthenticateRequest + { + #region PROPERTIES + + [JsonPropertyName("username_or_email")] + public string UsernameOrEmail { get; set; } + + [JsonPropertyName("password")] + public string Password { get; set; } + + [JsonPropertyName("remember_me")] + public bool RememberMe { get; set; } + + #endregion + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs new file mode 100644 index 0000000..0bd0636 --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace WatchIt.Shared.Models.Accounts.Authenticate +{ + public class AuthenticateResponse + { + #region PROPERTIES + + [JsonPropertyName("access_token")] + public required string AccessToken { get; init; } + + [JsonPropertyName("refresh_token")] + public required string RefreshToken { get; init; } + + #endregion + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs new file mode 100644 index 0000000..f1cb25d --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs @@ -0,0 +1,27 @@ +using FluentValidation; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace WatchIt.Shared.Models.Accounts.Register +{ + public class RegisterRequest + { + #region PROPERTIES + + [JsonPropertyName("username")] + public string Username { get; set; } + + [JsonPropertyName("email")] + public string Email { get; set; } + + [JsonPropertyName("password")] + public string Password { get; set; } + + #endregion + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs new file mode 100644 index 0000000..43be31e --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using WatchIt.Database.Model.Account; + +namespace WatchIt.Shared.Models.Accounts.Register +{ + public class RegisterResponse + { + #region PROPERTIES + + [JsonPropertyName("id")] + public required long Id { get; init; } + + [JsonPropertyName("username")] + public required string Username { get; init; } + + [JsonPropertyName("email")] + public required string Email { get; init; } + + [JsonPropertyName("creation_date")] + public required DateTime CreationDate { get; init; } + + #endregion + + + + #region CONVERTION + + public static implicit operator RegisterResponse(Account account) => new RegisterResponse + { + Id = account.Id, + Username = account.Username, + Email = account.Email, + CreationDate = account.CreationDate + }; + + #endregion + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs b/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs new file mode 100644 index 0000000..7bab44e --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs @@ -0,0 +1,107 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +namespace WatchIt.Shared.Models +{ + public class RequestResult + { + #region PROPERTIES + + public RequestResultStatus Status { get; } + public IEnumerable ValidationMessages { get; init; } + + #endregion + + + + #region CONSTRUCTORS + + internal RequestResult(RequestResultStatus status) => Status = status; + + public static RequestResult Ok() => new RequestResult(RequestResultStatus.Ok); + public static RequestResult Ok() => new RequestResult(RequestResultStatus.Ok); + public static RequestResult Ok(T data) => new RequestResult(RequestResultStatus.Ok) { Data = data }; + public static RequestResult Created(string location, T resource) => new RequestResult(RequestResultStatus.Created) { NewResourceLocation = location, Data = resource }; + public static RequestResult NoContent() => new RequestResult(RequestResultStatus.NoContent); + public static RequestResult NoContent() => new RequestResult(RequestResultStatus.NoContent); + public static RequestResult BadRequest(params string[] validationErrors) => new RequestResult(RequestResultStatus.BadRequest) { ValidationMessages = validationErrors }; + public static RequestResult BadRequest(params string[] validationErrors) => new RequestResult(RequestResultStatus.BadRequest) { ValidationMessages = validationErrors }; + public static RequestResult Unauthorized(params string[] validationErrors) => new RequestResult(RequestResultStatus.Unauthorized) { ValidationMessages = validationErrors }; + public static RequestResult Unauthorized(params string[] validationErrors) => new RequestResult(RequestResultStatus.Unauthorized) { ValidationMessages = validationErrors }; + public static RequestResult Forbidden() => new RequestResult(RequestResultStatus.Forbidden); + public static RequestResult Forbidden() => new RequestResult(RequestResultStatus.Forbidden); + public static RequestResult NotFound() => new RequestResult(RequestResultStatus.NotFound); + public static RequestResult NotFound() => new RequestResult(RequestResultStatus.NotFound); + public static RequestResult Conflict() => new RequestResult(RequestResultStatus.Conflict); + public static RequestResult Conflict() => new RequestResult(RequestResultStatus.Conflict); + + #endregion + + + + #region CONVERSION + + public static implicit operator ActionResult(RequestResult result) => result.Status switch + { + RequestResultStatus.Ok => HandleOk(result), + RequestResultStatus.NoContent => HandleNoContent(), + RequestResultStatus.BadRequest => HandleBadRequest(result), + RequestResultStatus.Unauthorized => HandleUnauthorized(result), + RequestResultStatus.Forbidden => HandleForbidden(), + RequestResultStatus.NotFound => HandleNotFound(), + RequestResultStatus.Conflict => HandleConflict(), + }; + + protected static ActionResult HandleOk(RequestResult result) => new OkResult(); + protected static ActionResult HandleNoContent() => new NoContentResult(); + protected static ActionResult HandleBadRequest(RequestResult result) => new BadRequestObjectResult(result.ValidationMessages); + protected static ActionResult HandleUnauthorized(RequestResult result) => new UnauthorizedObjectResult(result.ValidationMessages); + protected static ActionResult HandleForbidden() => new ForbidResult(); + protected static ActionResult HandleNotFound() => new NotFoundResult(); + protected static ActionResult HandleConflict() => new ConflictResult(); + + #endregion + } + + public class RequestResult : RequestResult + { + #region PROPERTIES + + public T? Data { get; init; } + public string? NewResourceLocation { get; init; } + + #endregion + + + + #region CONSTRUCTORS + + internal RequestResult(RequestResultStatus type) : base(type) { } + + #endregion + + + + #region CONVERSION + + public static implicit operator ActionResult(RequestResult result) => result.Status switch + { + RequestResultStatus.Ok => HandleOk(result), + RequestResultStatus.Created => HandleCreated(result), + RequestResultStatus.NoContent => HandleNoContent(), + RequestResultStatus.BadRequest => HandleBadRequest(result), + RequestResultStatus.Unauthorized => HandleUnauthorized(result), + RequestResultStatus.Forbidden => HandleForbidden(), + RequestResultStatus.NotFound => HandleNotFound(), + RequestResultStatus.Conflict => HandleConflict(), + }; + + private static ActionResult HandleOk(RequestResult result) => result.Data is null ? new OkResult() : new OkObjectResult(result.Data); + private static ActionResult HandleCreated(RequestResult result) => new CreatedResult(result.NewResourceLocation, result.Data); + + #endregion + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs b/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs new file mode 100644 index 0000000..3808ca5 --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.Shared.Models +{ + public enum RequestResultStatus + { + Ok = 200, + Created = 201, + NoContent = 204, + BadRequest = 400, + Unauthorized = 401, + Forbidden = 403, + NotFound = 404, + Conflict = 409, + } +} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj b/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj new file mode 100644 index 0000000..c5be2bb --- /dev/null +++ b/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs new file mode 100644 index 0000000..a592f2e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using WatchIt.Shared.Models.Accounts.Authenticate; +using WatchIt.Shared.Models.Accounts.Register; +using WatchIt.WebAPI.Services.Controllers; + +namespace WatchIt.WebAPI.Controllers +{ + [ApiController] + [Route("api/accounts")] + public class AccountsController(IAccountsControllerService accountsControllerService) : ControllerBase + { + #region METHODS + + [HttpPost] + [Route("register")] + [AllowAnonymous] + [ProducesResponseType(typeof(RegisterResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task Register([FromBody] RegisterRequest data) => await accountsControllerService.Register(data); + + [HttpPost] + [Route("authenticate")] + [AllowAnonymous] + [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task Authenticate([FromBody] AuthenticateRequest data) => await accountsControllerService.Authenticate(data); + + [HttpPost] + [Route("authenticate-refresh")] + [Authorize(AuthenticationSchemes = "refresh")] + [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task AuthenticateRefresh() => await accountsControllerService.AuthenticateRefresh(); + + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj new file mode 100644 index 0000000..54ffa42 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs new file mode 100644 index 0000000..7d98976 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs @@ -0,0 +1,88 @@ +using Microsoft.EntityFrameworkCore; +using SimpleToolkit.Extensions; +using System.Security.Cryptography; +using System.Text; +using WatchIt.Database; +using WatchIt.Database.Model.Account; +using WatchIt.Shared.Models; +using WatchIt.Shared.Models.Accounts.Authenticate; +using WatchIt.Shared.Models.Accounts.Register; +using WatchIt.WebAPI.Services.Utility.JWT; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace WatchIt.WebAPI.Services.Controllers +{ + public interface IAccountsControllerService + { + Task> Register(RegisterRequest data); + Task> Authenticate(AuthenticateRequest data); + Task> AuthenticateRefresh(); + } + + public class AccountsControllerService(IJWTService jwtService, DatabaseContext database) : IAccountsControllerService + { + #region PUBLIC METHODS + + public async Task> Register(RegisterRequest data) + { + string leftSalt = StringExtensions.CreateRandom(20); + string rightSalt = StringExtensions.CreateRandom(20); + byte[] hash = ComputeHash(data.Password, leftSalt, rightSalt); + + Account account = new Account + { + Username = data.Username, + Email = data.Email, + Password = hash, + LeftSalt = leftSalt, + RightSalt = rightSalt + }; + await database.Accounts.AddAsync(account); + await database.SaveChangesAsync(); + + return RequestResult.Created($"accounts/{account.Id}", account); + } + + public async Task> Authenticate(AuthenticateRequest data) + { + Account? account = await database.Accounts.FirstOrDefaultAsync(x => string.Equals(x.Email, data.UsernameOrEmail) || string.Equals(x.Username, data.UsernameOrEmail)); + if (account is null) + { + return RequestResult.Unauthorized("User does not exists"); + } + + byte[] hash = ComputeHash(data.Password, account.LeftSalt, account.RightSalt); + if (!Enumerable.SequenceEqual(hash, account.Password)) + { + return RequestResult.Unauthorized("Incorrect password"); + } + + Task refreshTokenTask = jwtService.CreateRefreshToken(account, true); + Task accessTokenTask = jwtService.CreateAccessToken(account); + await Task.WhenAll(refreshTokenTask, accessTokenTask); + + AuthenticateResponse response = new AuthenticateResponse + { + AccessToken = accessTokenTask.Result, + RefreshToken = refreshTokenTask.Result, + }; + + return RequestResult.Ok(response); + } + + public async Task> AuthenticateRefresh() + { + + } + + #endregion + + + + #region PRIVATE METHODS + + protected byte[] ComputeHash(string password, string leftSalt, string rightSalt) => SHA512.Create().ComputeHash(Encoding.UTF8.GetBytes($"{leftSalt}{password}{rightSalt}")); + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj new file mode 100644 index 0000000..6c3b4fd --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs new file mode 100644 index 0000000..88d3f95 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WatchIt.WebAPI.Services.Utility.Configuration.Models; + +namespace WatchIt.WebAPI.Services.Utility.Configuration +{ + public interface IConfigurationService + { + ConfigurationData Data { get; } + } + + + + public class ConfigurationService(IConfiguration configuration) : IConfigurationService + { + #region PROPERTIES + + public ConfigurationData Data => configuration.GetSection("WebAPI").Get()!; + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs new file mode 100644 index 0000000..0b65457 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.Configuration.Models +{ + public class AccessToken + { + public int Lifetime { get; set; } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs new file mode 100644 index 0000000..f68baf5 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.Configuration.Models +{ + public class Authentication + { + public string Key { get; set; } + public string Issuer { get; set; } + public RefreshToken RefreshToken { get; set; } + public AccessToken AccessToken { get; set; } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs new file mode 100644 index 0000000..faf1bec --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.Configuration.Models +{ + public class ConfigurationData + { + public Authentication Authentication { get; set; } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs new file mode 100644 index 0000000..542c115 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.Configuration.Models +{ + public class RefreshToken + { + public int Lifetime { get; set; } + public int ExtendedLifetime { get; set; } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj new file mode 100644 index 0000000..800b08f --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs new file mode 100644 index 0000000..e8afdc6 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs @@ -0,0 +1,131 @@ +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; +using WatchIt.Database; +using WatchIt.Database.Model.Account; +using WatchIt.WebAPI.Services.Utility.Configuration; + +namespace WatchIt.WebAPI.Services.Utility.JWT +{ + public interface IJWTService + { + Task CreateAccessToken(Account account); + Task CreateRefreshToken(Account account, bool extendable); + Task ExtendRefreshToken(Account account, Guid id); + } + + + + public class JWTService(IConfigurationService configurationService, DatabaseContext database) : IJWTService + { + #region PUBLIC METHODS + + public async Task CreateRefreshToken(Account account, bool extendable) + { + int expirationMinutes = extendable ? configurationService.Data.Authentication.RefreshToken.ExtendedLifetime : configurationService.Data.Authentication.RefreshToken.Lifetime; + DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes); + Guid id = Guid.NewGuid(); + + AccountRefreshToken refreshToken = new AccountRefreshToken + { + Id = id, + AccountId = account.Id, + ExpirationDate = expirationDate, + IsExtendable = extendable + }; + database.AccountRefreshTokens.Add(refreshToken); + Task saveTask = database.SaveChangesAsync(); + + SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate); + tokenDescriptor.Audience = "refresh"; + tokenDescriptor.Subject.AddClaim(new Claim("extend", extendable.ToString())); + + string tokenString = TokenToString(tokenDescriptor); + + await saveTask; + + return tokenString; + } + + public async Task ExtendRefreshToken(Account account, Guid id) + { + AccountRefreshToken? token = account.AccountRefreshTokens.FirstOrDefault(x => x.Id == id); + if (token is null) + { + throw new TokenNotFoundException(); + } + if (!token.IsExtendable) + { + throw new TokenNotExtendableException(); + } + + int expirationMinutes = configurationService.Data.Authentication.RefreshToken.ExtendedLifetime; + DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes); + + token.ExpirationDate = expirationDate; + + Task saveTask = database.SaveChangesAsync(); + + SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate); + tokenDescriptor.Audience = "refresh"; + tokenDescriptor.Subject.AddClaim(new Claim("extend", bool.TrueString)); + + string tokenString = TokenToString(tokenDescriptor); + + await saveTask; + + return tokenString; + } + + public async Task CreateAccessToken(Account account) + { + DateTime lifetime = DateTime.Now.AddMinutes(configurationService.Data.Authentication.AccessToken.Lifetime); + Guid id = Guid.NewGuid(); + + SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, lifetime); + tokenDescriptor.Audience = "access"; + tokenDescriptor.Subject.AddClaim(new Claim("admin", account.IsAdmin.ToString())); + + return TokenToString(tokenDescriptor); + } + + #endregion + + + + #region PRIVATE METHODS + + protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime) => new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new List + { + new Claim(JwtRegisteredClaimNames.Jti, id.ToString()), + new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()), + new Claim(JwtRegisteredClaimNames.Email, account.Email), + new Claim(JwtRegisteredClaimNames.UniqueName, account.Username), + new Claim(JwtRegisteredClaimNames.Exp, expirationTime.ToString()), + }), + Expires = expirationTime, + Issuer = configurationService.Data.Authentication.Issuer, + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512) + }; + + protected string TokenToString(SecurityTokenDescriptor tokenDescriptor) + { + System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); + handler.InboundClaimTypeMap.Clear(); + + SecurityToken token = handler.CreateToken(tokenDescriptor); + + return handler.WriteToken(token); + } + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs new file mode 100644 index 0000000..971cce2 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.JWT +{ + public class TokenNotExtendableException : Exception + { + public TokenNotExtendableException() : base() { } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs new file mode 100644 index 0000000..1e37897 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.JWT +{ + public class TokenNotFoundException : Exception + { + public TokenNotFoundException() : base() { } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj new file mode 100644 index 0000000..e3ca5ed --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs new file mode 100644 index 0000000..e0c8cd9 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Services.Utility.User +{ + public class UserService(IHttpContextAccessor accessor) + { + #region PUBLIC METHODS + + + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj new file mode 100644 index 0000000..297920e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs new file mode 100644 index 0000000..7f2bdfb --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs @@ -0,0 +1,26 @@ +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using WatchIt.Database; +using WatchIt.Shared.Models.Accounts.Authenticate; + +namespace WatchIt.WebAPI.Validators.Accounts +{ + public class AuthenticateRequestValidator : AbstractValidator + { + #region CONSTRUCTOR + + public AuthenticateRequestValidator(DatabaseContext database) + { + RuleFor(x => x.UsernameOrEmail).NotEmpty(); + RuleFor(x => x.Password).NotEmpty(); + } + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs new file mode 100644 index 0000000..ea90618 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs @@ -0,0 +1,31 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WatchIt.Database; +using WatchIt.Shared.Models.Accounts.Register; + +namespace WatchIt.WebAPI.Validators.Accounts +{ + public class RegisterRequestValidator : AbstractValidator + { + #region CONSTRUCTOR + + public RegisterRequestValidator(DatabaseContext database) + { + RuleFor(x => x.Username).MinimumLength(5) + .MaximumLength(50) + .CannotBeIn(database.Accounts, x => x.Username).WithMessage("Username was already used"); + RuleFor(x => x.Email).EmailAddress() + .CannotBeIn(database.Accounts, x => x.Email).WithMessage("Email was already used"); + RuleFor(x => x.Password).MinimumLength(8) + .Must(x => x.Any(c => Char.IsUpper(c))).WithMessage("Password must contain at least one uppercase letter.") + .Must(x => x.Any(c => Char.IsLower(c))).WithMessage("Password must contain at least one lowercase letter.") + .Must(x => x.Any(c => Char.IsDigit(c))).WithMessage("Password must contain at least one digit."); + } + + #endregion + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs new file mode 100644 index 0000000..76d157c --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs @@ -0,0 +1,18 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WatchIt.WebAPI.Validators +{ + public static class CustomValidators + { + public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => !collection.Any(e => Equals(e, x))); + public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => !collection.Select(propertyFunc).Any(e => Equals(e, x))); + public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => collection.Any(e => Equals(e, x))); + public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => collection.Select(propertyFunc).Any(e => Equals(e, x))); + + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj new file mode 100644 index 0000000..4498da0 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/WatchIt.sln b/WatchIt.sln index e01b1b8..3e1cdfd 100644 --- a/WatchIt.sln +++ b/WatchIt.sln @@ -15,7 +15,27 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.Database", "WatchIt EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.Database.Model", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.csproj", "{46A294FF-F15F-4773-9E33-AFFC6DF2148C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.DataSeeding", "WatchIt.Database\WatchIt.Database.DataSeeding\WatchIt.Database.DataSeeding.csproj", "{EC685BDC-9C80-4D6D-94DA-F788976CD104}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.Database.DataSeeding", "WatchIt.Database\WatchIt.Database.DataSeeding\WatchIt.Database.DataSeeding.csproj", "{EC685BDC-9C80-4D6D-94DA-F788976CD104}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.WebAPI.Controllers", "WatchIt.WebAPI\WatchIt.WebAPI.Controllers\WatchIt.WebAPI.Controllers.csproj", "{F8EC5C47-9866-4065-AA8B-0441280BB1C9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Shared", "WatchIt.Shared", "{02132B7F-2055-4FA9-AA94-EDB92159AFF8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.Shared.Models", "WatchIt.Shared\WatchIt.Shared.Models\WatchIt.Shared.Models.csproj", "{B3D90254-F594-4C15-B003-F828EEDA850A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services", "WatchIt.WebAPI.Services", "{CD2FA54C-BAF5-41B2-B8C7-5C3EDED1B41A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchIt.WebAPI.Services.Controllers", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.csproj", "{CE4669C8-E537-400A-A872-A32BD165F1AE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Validators", "WatchIt.WebAPI\WatchIt.WebAPI.Validators\WatchIt.WebAPI.Validators.csproj", "{E7DE3C54-B199-4DF8-ADA0-46FE85AA059F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services.Utility", "WatchIt.WebAPI.Services.Utility", "{70058164-43EB-47E5-8507-23D6A41A2581}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.Configuration", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Configuration\WatchIt.WebAPI.Services.Utility.Configuration.csproj", "{9915A61F-A7CA-4F6F-A213-4B31BE81C3DF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.JWT", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.JWT\WatchIt.WebAPI.Services.Utility.JWT.csproj", "{68A3C906-7470-40A1-B9C3-2F8963AA7E44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.User", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.User\WatchIt.WebAPI.Services.Utility.User.csproj", "{FA335A3A-BD57-472A-B3ED-43F18D9F31A6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,6 +59,34 @@ Global {EC685BDC-9C80-4D6D-94DA-F788976CD104}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC685BDC-9C80-4D6D-94DA-F788976CD104}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC685BDC-9C80-4D6D-94DA-F788976CD104}.Release|Any CPU.Build.0 = Release|Any CPU + {F8EC5C47-9866-4065-AA8B-0441280BB1C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8EC5C47-9866-4065-AA8B-0441280BB1C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8EC5C47-9866-4065-AA8B-0441280BB1C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8EC5C47-9866-4065-AA8B-0441280BB1C9}.Release|Any CPU.Build.0 = Release|Any CPU + {B3D90254-F594-4C15-B003-F828EEDA850A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3D90254-F594-4C15-B003-F828EEDA850A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3D90254-F594-4C15-B003-F828EEDA850A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3D90254-F594-4C15-B003-F828EEDA850A}.Release|Any CPU.Build.0 = Release|Any CPU + {CE4669C8-E537-400A-A872-A32BD165F1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE4669C8-E537-400A-A872-A32BD165F1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE4669C8-E537-400A-A872-A32BD165F1AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE4669C8-E537-400A-A872-A32BD165F1AE}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DE3C54-B199-4DF8-ADA0-46FE85AA059F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DE3C54-B199-4DF8-ADA0-46FE85AA059F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DE3C54-B199-4DF8-ADA0-46FE85AA059F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DE3C54-B199-4DF8-ADA0-46FE85AA059F}.Release|Any CPU.Build.0 = Release|Any CPU + {9915A61F-A7CA-4F6F-A213-4B31BE81C3DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9915A61F-A7CA-4F6F-A213-4B31BE81C3DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9915A61F-A7CA-4F6F-A213-4B31BE81C3DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9915A61F-A7CA-4F6F-A213-4B31BE81C3DF}.Release|Any CPU.Build.0 = Release|Any CPU + {68A3C906-7470-40A1-B9C3-2F8963AA7E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68A3C906-7470-40A1-B9C3-2F8963AA7E44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68A3C906-7470-40A1-B9C3-2F8963AA7E44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68A3C906-7470-40A1-B9C3-2F8963AA7E44}.Release|Any CPU.Build.0 = Release|Any CPU + {FA335A3A-BD57-472A-B3ED-43F18D9F31A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA335A3A-BD57-472A-B3ED-43F18D9F31A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA335A3A-BD57-472A-B3ED-43F18D9F31A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA335A3A-BD57-472A-B3ED-43F18D9F31A6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -47,6 +95,15 @@ Global {8CDAA140-05FC-4EB7-A9F5-A85032C8FD2F} = {98C91E27-2C36-4C74-A80F-9ACD7F28BC54} {46A294FF-F15F-4773-9E33-AFFC6DF2148C} = {98C91E27-2C36-4C74-A80F-9ACD7F28BC54} {EC685BDC-9C80-4D6D-94DA-F788976CD104} = {98C91E27-2C36-4C74-A80F-9ACD7F28BC54} + {F8EC5C47-9866-4065-AA8B-0441280BB1C9} = {76B40EBF-8054-4A15-ABE8-141E1CCA6E4E} + {B3D90254-F594-4C15-B003-F828EEDA850A} = {02132B7F-2055-4FA9-AA94-EDB92159AFF8} + {CD2FA54C-BAF5-41B2-B8C7-5C3EDED1B41A} = {76B40EBF-8054-4A15-ABE8-141E1CCA6E4E} + {CE4669C8-E537-400A-A872-A32BD165F1AE} = {CD2FA54C-BAF5-41B2-B8C7-5C3EDED1B41A} + {E7DE3C54-B199-4DF8-ADA0-46FE85AA059F} = {76B40EBF-8054-4A15-ABE8-141E1CCA6E4E} + {70058164-43EB-47E5-8507-23D6A41A2581} = {CD2FA54C-BAF5-41B2-B8C7-5C3EDED1B41A} + {9915A61F-A7CA-4F6F-A213-4B31BE81C3DF} = {70058164-43EB-47E5-8507-23D6A41A2581} + {68A3C906-7470-40A1-B9C3-2F8963AA7E44} = {70058164-43EB-47E5-8507-23D6A41A2581} + {FA335A3A-BD57-472A-B3ED-43F18D9F31A6} = {70058164-43EB-47E5-8507-23D6A41A2581} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D253492-C786-4DD9-80B5-7DE51D4D3304} diff --git a/WatchIt/Program.cs b/WatchIt/Program.cs index 3086beb..76e1439 100644 --- a/WatchIt/Program.cs +++ b/WatchIt/Program.cs @@ -1,5 +1,16 @@ +using FluentValidation; +using FluentValidation.AspNetCore; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Identity.Data; using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using System.Reflection; +using System.Text; using WatchIt.Database; +using WatchIt.Shared.Models.Accounts.Register; +using WatchIt.WebAPI.Services.Controllers; +using WatchIt.WebAPI.Services.Utility.Configuration; +using WatchIt.WebAPI.Services.Utility.JWT; using WatchIt.Website; namespace WatchIt @@ -23,10 +34,7 @@ namespace WatchIt ConfigureLogging(); ConfigureDatabase(); ConfigureWebAPI(); - - // Add services to the container. - _builder.Services.AddRazorComponents() - .AddInteractiveServerComponents(); + ConfigureWebsite(); var app = _builder.Build(); @@ -85,11 +93,86 @@ namespace WatchIt protected static void ConfigureWebAPI() { - _builder.Services.AddControllers(); + _builder.Services.AddValidatorsFromAssembly(Assembly.Load("WatchIt.Shared.Models")); + + _builder.Services.AddAuthentication(x => + { + x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(x => + { + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_builder.Configuration.GetValue("WebAPI:Authentication:Key"))), + ValidateAudience = true, + ValidAudience = "access", + ValidIssuer = _builder.Configuration.GetValue("WebAPI:Authentication:Issuer"), + ValidateLifetime = true, + ClockSkew = TimeSpan.FromMinutes(1), + }; + x.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Add("Token-Expired", "true"); + } + return Task.CompletedTask; + } + }; + }) + .AddJwtBearer("refresh", x => + { + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_builder.Configuration.GetValue("WebAPI:Authentication:Key"))), + ValidateAudience = true, + ValidIssuer = _builder.Configuration.GetValue("WebAPI:Authentication:Issuer"), + ValidAudience = "refresh", + ValidateLifetime = true, + ClockSkew = TimeSpan.FromMinutes(1) + }; + x.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Add("Token-Expired", "true"); + } + return Task.CompletedTask; + } + }; + }); + _builder.Services.AddAuthorization(); + + _builder.Services.AddHttpContextAccessor(); + + _builder.Services.AddSingleton(); + _builder.Services.AddSingleton(); + + _builder.Services.AddSingleton(); + + _builder.Services.AddFluentValidationAutoValidation(); _builder.Services.AddEndpointsApiExplorer(); + _builder.Services.AddControllers(); _builder.Services.AddSwaggerGen(); } + protected static void ConfigureWebsite() + { + _builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); + } + #endregion } } diff --git a/WatchIt/WatchIt.csproj b/WatchIt/WatchIt.csproj index 8207f5d..75368ab 100644 --- a/WatchIt/WatchIt.csproj +++ b/WatchIt/WatchIt.csproj @@ -7,6 +7,11 @@ + + + + + all @@ -26,7 +31,15 @@ + + + + + + + + diff --git a/WatchIt/WebAPI/AccountsController.cs b/WatchIt/WebAPI/AccountsController.cs deleted file mode 100644 index c897fdf..0000000 --- a/WatchIt/WebAPI/AccountsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace WatchIt.WebAPI -{ - [ApiController] - [Route("api/accounts")] - public class AccountsController : ControllerBase - { - [HttpPost] - [Route("create-account")] - public async Task CreateAccount() - { - } - } -} diff --git a/WatchIt/appsettings.json b/WatchIt/appsettings.json index b01ad1d..5647fb9 100644 --- a/WatchIt/appsettings.json +++ b/WatchIt/appsettings.json @@ -1,12 +1,28 @@ { + "WebAPI": { + "Authentication": { + "Key": "testkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytest", + "Issuer": "WatchIt", + "RefreshToken": { + "Lifetime": 1440, + "ExtendedLifetime": 10080 + }, + "AccessToken": { + "Lifetime": 5 + } + } + }, + "Website": { + + }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*", "ConnectionStrings": { "Default": "Host=localhost;Database=watchit;Username=watchit;Password=Xdv2Etchavbuuho" - } + }, + "AllowedHosts": "*" } diff --git a/genre.json b/genre.json deleted file mode 100644 index acb126a..0000000 --- a/genre.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "Id": 1, - "Name": "Comedy" - }, - { - "Id": 2, - "Name": "Thriller" - }, - { - "Id": 3, - "Name": "Horror" - }, - { - "Id": 4, - "Name": "Action" - } -] \ No newline at end of file From 4b333878b8ac7a9356367142931c931c2d8e3477 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sat, 27 Apr 2024 22:36:16 +0200 Subject: [PATCH 3/5] project reorganized --- .dockerignore | 25 + .gitignore | 1 + .../Accounts/AuthenticateRequest.cs | 19 + .../Accounts/AuthenticateResponse.cs | 16 + .../Accounts/RegisterRequest.cs | 15 + .../Accounts/RegisterResponse.cs | 39 + .../WatchIt.Common.Model/Genres/Genre.cs | 12 + .../Genres/GenreQueryParameters.cs | 29 + .../Genres/GenreRequest.cs | 20 + .../Genres/GenreResponse.cs | 28 + .../WatchIt.Common.Model/Movies/Movie.cs | 24 + .../Movies/MovieQueryParameters.cs | 67 + .../Movies/MovieRequest.cs | 39 + .../Movies/MovieResponse.cs | 31 + .../WatchIt.Common.Model/QueryParameters.cs | 118 ++ .../WatchIt.Common.Model.csproj | 17 + .../Data/Country.json | 10 - .../DataReader.cs | 25 - .../WatchIt.Database.DataSeeding.csproj | 13 - .../WatchIt.Database.Model/Account/Account.cs | 121 -- .../Account/AccountProfilePicture.cs | 59 - .../Account/AccountRefreshToken.cs | 57 - .../WatchIt.Database.Model/Common/Country.cs | 56 - .../WatchIt.Database.Model/Common/Gender.cs | 41 - .../WatchIt.Database.Model/Common/Genre.cs | 61 - .../WatchIt.Database.Model/EntityBuilder.cs | 24 - .../WatchIt.Database.Model/IEntity.cs | 20 - .../WatchIt.Database.Model/Media/Media.cs | 95 -- .../Media/MediaGenre.cs | 52 - .../Media/MediaMovie.cs | 50 - .../Media/MediaPhotoImage.cs | 76 - .../Media/MediaPosterImage.cs | 57 - .../Media/MediaProductionCountry.cs | 52 - .../Media/MediaSeries.cs | 49 - .../Media/MediaSeriesEpisode.cs | 64 - .../Media/MediaSeriesSeason.cs | 60 - .../WatchIt.Database.Model/Person/Person.cs | 81 - .../Person/PersonActorRole.cs | 72 - .../Person/PersonActorRoleType.cs | 41 - .../Person/PersonCreatorRole.cs | 69 - .../Person/PersonCreatorRoleType.cs | 41 - .../Person/PersonPhotoImage.cs | 58 - .../Rating/RatingMedia.cs | 62 - .../Rating/RatingMediaSeriesEpisode.cs | 63 - .../Rating/RatingMediaSeriesSeason.cs | 64 - .../Rating/RatingPersonActorRole.cs | 66 - .../Rating/RatingPersonCreatorRole.cs | 66 - .../ViewCount/ViewCountMedia.cs | 62 - .../ViewCount/ViewCountPerson.cs | 62 - .../Account/AccountConfiguration.cs | 66 + .../AccountProfilePictureConfiguration.cs | 29 + .../AccountRefreshTokenConfiguration.cs | 30 + .../Common/CountryConfiguration.cs | 35 + .../Common/GenderConfiguration.cs | 24 + .../Common/GenreConfiguration.cs | 34 + .../Media/MediaConfiguration.cs | 44 + .../Media/MediaGenreConfiguration.cs | 25 + .../Media/MediaMovieConfiguration.cs | 24 + .../Media/MediaPhotoImageConfiguration.cs | 44 + .../Media/MediaPosterImageConfiguration.cs | 29 + .../MediaProductionCountryConfiguration.cs | 25 + .../Media/MediaSeriesConfiguration.cs | 25 + .../Media/MediaSeriesEpisodeConfiguration.cs | 33 + .../Media/MediaSeriesSeasonConfiguration.cs | 29 + .../Person/PersonActorRoleConfiguration.cs | 38 + .../PersonActorRoleTypeConfiguration.cs | 25 + .../Person/PersonConfiguration.cs | 40 + .../Person/PersonCreatorRoleConfiguration.cs | 38 + .../PersonCreatorRoleTypeConfiguration.cs | 25 + .../Person/PersonPhotoImageConfiguration.cs | 29 + .../Rating/RatingMediaConfiguration.cs | 34 + .../RatingMediaSeriesEpisodeConfiguration.cs | 34 + .../RatingMediaSeriesSeasonConfiguration.cs | 34 + .../RatingPersonActorRoleConfiguration.cs | 36 + .../RatingPersonCreatorRoleConfiguration.cs | 36 + .../ViewCount/ViewCountMediaConfiguration.cs | 34 + .../ViewCount/ViewCountPersonConfiguration.cs | 34 + ...atchIt.Database.Model.Configuration.csproj | 19 + .../Data/Country.json | 12 + .../Data/Gender.json | 0 .../Data/Genre.json | 0 .../Data/PersonActorRoleType.json | 0 .../Data/PersonCreatorRoleType.json | 0 .../DataReader.cs | 23 + .../WatchIt.Database.Model.Seeding.csproj | 9 + .../WatchIt.Database.Model.csproj | 20 - .../WatchIt.Database.Model/Account/Account.cs | 44 + .../Account/AccountProfilePicture.cs | 21 + .../Account/AccountRefreshToken.cs | 21 + .../WatchIt.Database.Model/Common/Country.cs | 23 + .../WatchIt.Database.Model/Common/Gender.cs | 11 + .../WatchIt.Database.Model/Common/Genre.cs | 23 + .../WatchIt.Database.Model/Media/Media.cs | 45 + .../Media/MediaGenre.cs | 22 + .../Media/MediaMovie.cs | 19 + .../Media/MediaPhotoImage.cs | 24 + .../Media/MediaPosterImage.cs | 21 + .../Media/MediaProductionCountry.cs | 22 + .../Media/MediaSeries.cs | 20 + .../Media/MediaSeriesEpisode.cs | 25 + .../Media/MediaSeriesSeason.cs | 25 + .../WatchIt.Database.Model/Person/Person.cs | 32 + .../Person/PersonActorRole.cs | 28 + .../Person/PersonActorRoleType.cs | 11 + .../Person/PersonCreatorRole.cs | 26 + .../Person/PersonCreatorRoleType.cs | 11 + .../Person/PersonPhotoImage.cs | 21 + .../Rating/RatingMedia.cs | 22 + .../Rating/RatingMediaSeriesEpisode.cs | 24 + .../Rating/RatingMediaSeriesSeason.cs | 24 + .../Rating/RatingPersonActorRole.cs | 24 + .../Rating/RatingPersonCreatorRole.cs | 24 + .../ViewCount/ViewCountMedia.cs | 21 + .../ViewCount/ViewCountPerson.cs | 21 + .../WatchIt.Database.Model.csproj | 9 + .../WatchIt.Database/DatabaseContext.cs | 218 ++- .../20240321165113_0000_Initial.Designer.cs | 1198 --------------- ...1165250_0001_GendersTableAdded.Designer.cs | 1213 --------------- .../20240321165250_0001_GendersTableAdded.cs | 123 -- ...1215_0002_ViewCountTablesAdded.Designer.cs | 1297 ---------------- ...0240321171215_0002_ViewCountTablesAdded.cs | 87 -- ...rNotRequiredAndDefaultNotAdmin.Designer.cs | 1297 ---------------- ...003_GenderNotRequiredAndDefaultNotAdmin.cs | 77 - ...004_AccountDescriptionNullable.Designer.cs | 1296 ---------------- ...4144605_0004_AccountDescriptionNullable.cs | 40 - ...0005_AccountRefreshTokensAdded.Designer.cs | 1331 ----------------- ...24195235_0005_AccountRefreshTokensAdded.cs | 52 - ...4205952_0006_AccountRefreshTokenChanges.cs | 39 - ...> 20240418190404_0001_Initial.Designer.cs} | 103 +- ...tial.cs => 20240418190404_0001_Initial.cs} | 412 +++-- .../DatabaseContextModelSnapshot.cs | 99 +- .../WatchIt.Database/WatchIt.Database.csproj | 51 +- .../Authenticate/AuthenticateRequest.cs | 26 - .../Authenticate/AuthenticateResponse.cs | 22 - .../Accounts/Register/RegisterRequest.cs | 27 - .../Accounts/Register/RegisterResponse.cs | 44 - .../WatchIt.Shared.Models/RequestResult.cs | 107 -- .../RequestResultStatus.cs | 20 - .../WatchIt.Shared.Models.csproj | 18 - .../AccountsController.cs | 70 +- .../GenresController.cs | 48 + .../MoviesController.cs | 68 + .../WatchIt.WebAPI.Controllers.csproj | 26 +- .../AccountsControllerService.cs | 88 -- .../AccountsControllerService.cs | 109 ++ .../IAccountsControllerService.cs | 11 + ...ebAPI.Services.Controllers.Accounts.csproj | 22 + .../RequestBadRequestResult.cs | 44 + .../RequestConflictResult.cs | 22 + .../RequestCreatedResult.cs | 33 + .../RequestForbiddenResult.cs | 22 + .../RequestNoContentResult.cs | 22 + .../RequestNotFoundResult.cs | 22 + .../RequestOkResult.cs | 47 + .../RequestResult.cs | 40 + .../RequestResultStatus.cs | 13 + .../RequestUnauthorizedResult.cs | 23 + ....WebAPI.Services.Controllers.Common.csproj | 19 + .../GenresControllerService.cs | 93 ++ .../IGenresControllerService.cs | 13 + ....WebAPI.Services.Controllers.Genres.csproj | 16 + .../IMoviesControllerService.cs | 16 + .../MoviesControllerService.cs | 171 +++ ....WebAPI.Services.Controllers.Movies.csproj | 16 + ...WatchIt.WebAPI.Services.Controllers.csproj | 20 - .../ConfigurationService.cs | 29 +- .../IConfigurationService.cs | 8 + .../Model/Authentication.cs | 8 + .../Model/ConfigurationData.cs | 10 + .../Model/ConnectionStrings.cs | 6 + .../Model/Console.cs | 6 + .../Model/FormatterOptions.cs | 6 + .../Model/LogLevel.cs | 8 + .../Model/Logging.cs | 7 + .../Model/RootUser.cs | 8 + .../Model/Token.cs | 7 + .../Model/Tokens.cs | 7 + .../Models/AccessToken.cs | 13 - .../Models/Authentication.cs | 16 - .../Models/ConfigurationData.cs | 13 - .../Models/RefreshToken.cs | 14 - ...bAPI.Services.Utility.Configuration.csproj | 17 +- .../JWTService.cs | 131 -- .../TokenNotExtendableException.cs | 13 - .../TokenNotFoundException.cs | 13 - ...WatchIt.WebAPI.Services.Utility.JWT.csproj | 21 - .../Exceptions/TokenNotExtendableException.cs | 6 + .../Exceptions/TokenNotFoundException.cs | 6 + .../ITokensService.cs | 11 + .../TokensService.cs | 115 ++ ...chIt.WebAPI.Services.Utility.Tokens.csproj | 19 + .../UserService.cs | 46 +- ...atchIt.WebAPI.Services.Utility.User.csproj | 21 +- .../Accounts/AuthenticateRequestValidator.cs | 29 +- .../Accounts/RegisterRequestValidator.cs | 40 +- .../CustomValidators.cs | 23 +- .../Genres/GenreRequestValidator.cs | 14 + .../WatchIt.WebAPI.Validators.csproj | 26 +- .../DeleteExpiredRefreshTokensService.cs | 30 + .../WatchIt.WebAPI.WorkerServices.csproj | 17 + WatchIt.WebAPI/WatchIt.WebAPI/Dockerfile | 23 + WatchIt.WebAPI/WatchIt.WebAPI/Program.cs | 161 ++ .../Properties/launchSettings.json | 41 + .../WatchIt.WebAPI/WatchIt.WebAPI.csproj | 36 + .../WatchIt.WebAPI/appsettings.json | 36 + .../WatchIt.Website/Components/App.razor | 20 + .../Components/Layout/MainLayout.razor | 23 + .../Components/Layout/MainLayout.razor.css | 96 ++ .../Components/Layout/NavMenu.razor | 29 + .../Components/Layout/NavMenu.razor.css | 105 ++ .../Components/Pages/Counter.razor | 19 + .../Components}/Pages/Error.razor | 6 +- .../Components/Pages/Home.razor | 7 + .../Components/Pages/Weather.razor | 66 + .../WatchIt.Website/Components}/Routes.razor | 6 +- .../Components}/_Imports.razor | 2 +- WatchIt.Website/WatchIt.Website/Dockerfile | 23 + WatchIt.Website/WatchIt.Website/Program.cs | 35 + .../Properties/launchSettings.json | 8 +- .../WatchIt.Website/WatchIt.Website.csproj | 16 + .../WatchIt.Website/appsettings.json | 3 +- .../WatchIt.Website}/wwwroot/app.css | 22 + .../wwwroot/bootstrap/bootstrap.min.css | 7 + .../wwwroot/bootstrap/bootstrap.min.css.map | 1 + .../WatchIt.Website/wwwroot/favicon.png | Bin 0 -> 1148 bytes WatchIt.sln | 206 ++- WatchIt/Program.cs | 178 --- WatchIt/WatchIt.csproj | 45 - WatchIt/Website/App.razor | 18 - WatchIt/Website/Layout/MainLayout.razor | 9 - WatchIt/Website/Layout/MainLayout.razor.css | 18 - WatchIt/Website/Pages/Home.razor | 10 - WatchIt/appsettings.json | 28 - 233 files changed, 4916 insertions(+), 11471 deletions(-) create mode 100644 .dockerignore create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateRequest.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateResponse.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterRequest.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterResponse.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Genres/Genre.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Genres/GenreRequest.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Movies/Movie.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Movies/MovieRequest.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/QueryParameters.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/WatchIt.Common.Model.csproj delete mode 100644 WatchIt.Database/WatchIt.Database.DataSeeding/Data/Country.json delete mode 100644 WatchIt.Database/WatchIt.Database.DataSeeding/DataReader.cs delete mode 100644 WatchIt.Database/WatchIt.Database.DataSeeding/WatchIt.Database.DataSeeding.csproj delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Account/Account.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Account/AccountProfilePicture.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Common/Country.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Common/Gender.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Common/Genre.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/EntityBuilder.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/IEntity.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/Media.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaGenre.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaMovie.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaPhotoImage.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaPosterImage.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaProductionCountry.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaSeries.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesSeason.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/Person.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRole.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRoleType.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRole.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Person/PersonPhotoImage.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Rating/RatingMedia.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs delete mode 100644 WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountProfilePictureConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountRefreshTokenConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/CountryConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenderConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenreConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaGenreConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaMovieConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPhotoImageConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPosterImageConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaProductionCountryConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesEpisodeConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesSeasonConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleTypeConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleTypeConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonPhotoImageConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesEpisodeConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesSeasonConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonActorRoleConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonCreatorRoleConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountMediaConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountPersonConfiguration.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/WatchIt.Database.Model.Configuration.csproj create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Country.json rename WatchIt.Database/{WatchIt.Database.DataSeeding => WatchIt.Database.Model/WatchIt.Database.Model.Seeding}/Data/Gender.json (100%) rename WatchIt.Database/{WatchIt.Database.DataSeeding => WatchIt.Database.Model/WatchIt.Database.Model.Seeding}/Data/Genre.json (100%) rename WatchIt.Database/{WatchIt.Database.DataSeeding => WatchIt.Database.Model/WatchIt.Database.Model.Seeding}/Data/PersonActorRoleType.json (100%) rename WatchIt.Database/{WatchIt.Database.DataSeeding => WatchIt.Database.Model/WatchIt.Database.Model.Seeding}/Data/PersonCreatorRoleType.json (100%) create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/DataReader.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/WatchIt.Database.Model.Seeding.csproj delete mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.csproj create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountProfilePicture.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountRefreshToken.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Country.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Gender.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Genre.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/Media.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaGenre.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaMovie.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPhotoImage.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPosterImage.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaProductionCountry.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeries.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesSeason.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/Person.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRole.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRoleType.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRole.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonPhotoImage.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMedia.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs create mode 100644 WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/WatchIt.Database.Model.csproj delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs delete mode 100644 WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs rename WatchIt.Database/WatchIt.Database/Migrations/{20240324205952_0006_AccountRefreshTokenChanges.Designer.cs => 20240418190404_0001_Initial.Designer.cs} (95%) rename WatchIt.Database/WatchIt.Database/Migrations/{20240321165113_0000_Initial.cs => 20240418190404_0001_Initial.cs} (86%) delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs delete mode 100644 WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Controllers/GenresController.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/WatchIt.WebAPI.Services.Controllers.Accounts.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestBadRequestResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestConflictResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestCreatedResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestForbiddenResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNoContentResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNotFoundResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestOkResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResultStatus.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestUnauthorizedResult.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/WatchIt.WebAPI.Services.Controllers.Common.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/GenresControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/IGenresControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/WatchIt.WebAPI.Services.Controllers.Genres.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/IMoviesControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/WatchIt.WebAPI.Services.Controllers.Movies.csproj delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/IConfigurationService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Authentication.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConfigurationData.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConnectionStrings.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Console.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/FormatterOptions.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/LogLevel.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Logging.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/RootUser.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Token.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Tokens.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs delete mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotExtendableException.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotFoundException.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/ITokensService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/TokensService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/WatchIt.WebAPI.Services.Utility.Tokens.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Genres/GenreRequestValidator.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/DeleteExpiredRefreshTokensService.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/WatchIt.WebAPI.WorkerServices.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI/Dockerfile create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI/Program.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI/Properties/launchSettings.json create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI/appsettings.json create mode 100644 WatchIt.Website/WatchIt.Website/Components/App.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor.css create mode 100644 WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor.css create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/Counter.razor rename {WatchIt/Website => WatchIt.Website/WatchIt.Website/Components}/Pages/Error.razor (93%) create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor rename {WatchIt/Website => WatchIt.Website/WatchIt.Website/Components}/Routes.razor (62%) rename {WatchIt/Website => WatchIt.Website/WatchIt.Website/Components}/_Imports.razor (91%) create mode 100644 WatchIt.Website/WatchIt.Website/Dockerfile create mode 100644 WatchIt.Website/WatchIt.Website/Program.cs rename {WatchIt => WatchIt.Website/WatchIt.Website}/Properties/launchSettings.json (80%) create mode 100644 WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj rename WatchIt/appsettings.Development.json => WatchIt.Website/WatchIt.Website/appsettings.json (80%) rename {WatchIt => WatchIt.Website/WatchIt.Website}/wwwroot/app.css (83%) create mode 100644 WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css create mode 100644 WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css.map create mode 100644 WatchIt.Website/WatchIt.Website/wwwroot/favicon.png delete mode 100644 WatchIt/Program.cs delete mode 100644 WatchIt/WatchIt.csproj delete mode 100644 WatchIt/Website/App.razor delete mode 100644 WatchIt/Website/Layout/MainLayout.razor delete mode 100644 WatchIt/Website/Layout/MainLayout.razor.css delete mode 100644 WatchIt/Website/Pages/Home.razor delete mode 100644 WatchIt/appsettings.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd967fc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8a30d25..0bdc52e 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +.idea/ \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateRequest.cs new file mode 100644 index 0000000..4be0f95 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateRequest.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class AuthenticateRequest +{ + #region PROPERTIES + + [JsonPropertyName("username_or_email")] + public required string UsernameOrEmail { get; set; } + + [JsonPropertyName("password")] + public required string Password { get; set; } + + [JsonPropertyName("remember_me")] + public bool RememberMe { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateResponse.cs new file mode 100644 index 0000000..09d3502 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AuthenticateResponse.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class AuthenticateResponse +{ + #region PROPERTIES + + [JsonPropertyName("access_token")] + public required string AccessToken { get; init; } + + [JsonPropertyName("refresh_token")] + public required string RefreshToken { get; init; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterRequest.cs new file mode 100644 index 0000000..808efe4 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterRequest.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class RegisterRequest +{ + [JsonPropertyName("username")] + public required string Username { get; set; } + + [JsonPropertyName("email")] + public required string Email { get; set; } + + [JsonPropertyName("password")] + public required string Password { get; set; } +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterResponse.cs new file mode 100644 index 0000000..b08af2e --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/RegisterResponse.cs @@ -0,0 +1,39 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using WatchIt.Database.Model.Account; + +namespace WatchIt.Common.Model.Accounts; + +public class RegisterResponse +{ + #region PROPERTIES + + [JsonPropertyName("id")] + public required long Id { get; init; } + + [JsonPropertyName("username")] + public required string Username { get; init; } + + [JsonPropertyName("email")] + public required string Email { get; init; } + + [JsonPropertyName("creation_date")] + public required DateTime CreationDate { get; init; } + + #endregion + + + + #region CONSTRUCTORS + + [SetsRequiredMembers] + public RegisterResponse(Account account) + { + Id = account.Id; + Username = account.Username; + Email = account.Email; + CreationDate = account.CreationDate; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/Genre.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/Genre.cs new file mode 100644 index 0000000..2a6a26f --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/Genre.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Genres; + +public class Genre +{ + [JsonPropertyName("name")] + public required string Name { get; set; } + + [JsonPropertyName("description")] + public string? Description { get; set; } +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs new file mode 100644 index 0000000..9b70ddb --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.Common.Model.Genres; + +public class GenreQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "name")] + public string? Name { get; set; } + + [FromQuery(Name = "description")] + public string? Description { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override bool IsMeetingConditions(GenreResponse item) => + ( + TestString(item.Name, Name) + && + TestString(item.Description, Description) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreRequest.cs new file mode 100644 index 0000000..27e771c --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreRequest.cs @@ -0,0 +1,20 @@ +namespace WatchIt.Common.Model.Genres; + +public class GenreRequest : Genre +{ + #region PUBLIC METHODS + + public Database.Model.Common.Genre CreateGenre() => new Database.Model.Common.Genre + { + Name = Name, + Description = Description, + }; + + public void UpdateGenre(Database.Model.Common.Genre genre) + { + genre.Name = Name; + genre.Description = Description; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs new file mode 100644 index 0000000..469e317 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs @@ -0,0 +1,28 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Genres; + +public class GenreResponse : Genre +{ + #region PROPERTIES + + [JsonPropertyName("id")] + public long Id { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + [SetsRequiredMembers] + public GenreResponse(Database.Model.Common.Genre genre) + { + Id = genre.Id; + Name = genre.Name; + Description = genre.Description; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/Movie.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/Movie.cs new file mode 100644 index 0000000..26fa9ff --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/Movie.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Movies; + +public class Movie +{ + [JsonPropertyName("title")] + public required string Title { get; set; } + + [JsonPropertyName("original_title")] + public string? OriginalTitle { get; set; } + + [JsonPropertyName("description")] + public string? Description { get; set; } + + [JsonPropertyName("release_date")] + public DateOnly? ReleaseDate { get; set; } + + [JsonPropertyName("length")] + public short? Length { get; set; } + + [JsonPropertyName("budget")] + public decimal? Budget { get; set; } +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs new file mode 100644 index 0000000..11f01e3 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.Common.Model.Movies; + +public class MovieQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "title")] + public string? Title { get; set; } + + [FromQuery(Name = "original_title")] + public string? OriginalTitle { get; set; } + + [FromQuery(Name = "description")] + public string? Description { get; set; } + + [FromQuery(Name = "release_date")] + public DateOnly? ReleaseDate { get; set; } + + [FromQuery(Name = "release_date_from")] + public DateOnly? ReleaseDateFrom { get; set; } + + [FromQuery(Name = "release_date_to")] + public DateOnly? ReleaseDateTo { get; set; } + + [FromQuery(Name = "length")] + public short? Length { get; set; } + + [FromQuery(Name = "length_from")] + public short? LengthFrom { get; set; } + + [FromQuery(Name = "length_to")] + public short? LengthTo { get; set; } + + [FromQuery(Name = "budget")] + public decimal? Budget { get; set; } + + [FromQuery(Name = "budget_from")] + public decimal? BudgetFrom { get; set; } + + [FromQuery(Name = "budget_to")] + public decimal? BudgetTo { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override bool IsMeetingConditions(MovieResponse item) => + ( + TestString(item.Title, Title) + && + TestString(item.OriginalTitle, OriginalTitle) + && + TestString(item.Description, Description) + && + TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo) + && + TestComparable(item.Length, Length, LengthFrom, LengthTo) + && + TestComparable(item.Budget, Budget, BudgetFrom, BudgetTo) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieRequest.cs new file mode 100644 index 0000000..c357054 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieRequest.cs @@ -0,0 +1,39 @@ +using WatchIt.Database.Model.Media; + +namespace WatchIt.Common.Model.Movies; + +public class MovieRequest : Movie +{ + #region PUBLIC METHODS + + public Media CreateMedia() => new Media + { + Title = Title, + OriginalTitle = OriginalTitle, + Description = Description, + ReleaseDate = ReleaseDate, + Length = Length, + }; + + public MediaMovie CreateMediaMovie(long id) => new MediaMovie + { + Id = id, + Budget = Budget, + }; + + public void UpdateMedia(Media media) + { + media.Title = Title; + media.OriginalTitle = OriginalTitle; + media.Description = Description; + media.ReleaseDate = ReleaseDate; + media.Length = Length; + } + + public void UpdateMediaMovie(MediaMovie mediaMovie) + { + mediaMovie.Budget = Budget; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs new file mode 100644 index 0000000..f1b4724 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Common.Model.Movies; + +public class MovieResponse : Movie +{ + #region PROPERTIES + + public long Id { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + [SetsRequiredMembers] + public MovieResponse(MediaMovie mediaMovie) + { + Id = mediaMovie.Media.Id; + Title = mediaMovie.Media.Title; + OriginalTitle = mediaMovie.Media.OriginalTitle; + Description = mediaMovie.Media.Description; + ReleaseDate = mediaMovie.Media.ReleaseDate; + Length = mediaMovie.Media.Length; + Budget = mediaMovie.Budget; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/QueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/QueryParameters.cs new file mode 100644 index 0000000..b3d983b --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/QueryParameters.cs @@ -0,0 +1,118 @@ +using System.Reflection; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.Common.Model; + +public abstract class QueryParameters where T : class +{ + #region PROPERTIES + + [FromQuery(Name = "order_by")] + public string? OrderBy { get; set; } + + [FromQuery(Name = "order")] + public string? Order { get; set; } + + [FromQuery(Name = "first")] + public int? First { get; set; } + + [FromQuery(Name = "after")] + public int? After { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public abstract bool IsMeetingConditions(T item); + + public IEnumerable PrepareData(IEnumerable data) + { + data = data.Where(IsMeetingConditions); + + if (OrderBy is not null) + { + PropertyInfo[] properties = typeof(T).GetProperties(); + foreach (PropertyInfo property in properties) + { + JsonPropertyNameAttribute? attribute = property.GetCustomAttributes(true).FirstOrDefault(); + if (attribute is not null && attribute.Name == OrderBy) + { + if (Order == "asc") + { + data = data.OrderBy(property.GetValue); + } + else + { + data = data.OrderByDescending(property.GetValue); + } + break; + } + } + } + if (After is not null) + { + data = data.Skip(After.Value); + } + if (First is not null) + { + data = data.Take(First.Value); + } + return data; + } + + #endregion + + + + #region PRIVATE METHODS + + protected bool TestString(string? property, string? regexQuery) => + ( + string.IsNullOrEmpty(regexQuery) + || + ( + !string.IsNullOrEmpty(property) + && + new Regex(regexQuery).IsMatch(property) + ) + ); + + protected bool TestComparable(IComparable? property, IComparable? exact, IComparable? from, IComparable? to) => + ( + ( + exact is null + || + ( + property is not null + && + property.CompareTo(exact) == 0 + ) + ) + && + ( + from is null + || + ( + property is not null + && + property.CompareTo(from) > 0 + ) + ) + && + ( + to is null + || + ( + property is not null + && + property.CompareTo(to) < 0 + ) + ) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/WatchIt.Common.Model.csproj b/WatchIt.Common/WatchIt.Common.Model/WatchIt.Common.Model.csproj new file mode 100644 index 0000000..dc1e5db --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/WatchIt.Common.Model.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/Country.json b/WatchIt.Database/WatchIt.Database.DataSeeding/Data/Country.json deleted file mode 100644 index 39bba35..0000000 --- a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/Country.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "Id": 1, - "Name": "Afghanistan" - }, - { - "Id": 2, - "Name": "Albania" - } -] \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/DataReader.cs b/WatchIt.Database/WatchIt.Database.DataSeeding/DataReader.cs deleted file mode 100644 index 6a728f7..0000000 --- a/WatchIt.Database/WatchIt.Database.DataSeeding/DataReader.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.DataSeeding -{ - public static class DataReader - { - #region METHODS - - public static IEnumerable Read() => Read(typeof(T).Name); - public static IEnumerable Read(string filename) - { - string jsonFile = $"..\\WatchIt.Database\\WatchIt.Database.DataSeeding\\Data\\{filename}.json"; - string dataString = File.ReadAllText(jsonFile); - IEnumerable data = JsonConvert.DeserializeObject>(dataString); - return data; - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/WatchIt.Database.DataSeeding.csproj b/WatchIt.Database/WatchIt.Database.DataSeeding/WatchIt.Database.DataSeeding.csproj deleted file mode 100644 index 3b5b170..0000000 --- a/WatchIt.Database/WatchIt.Database.DataSeeding/WatchIt.Database.DataSeeding.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - diff --git a/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs b/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs deleted file mode 100644 index abf2064..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Account/Account.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Common; -using WatchIt.Database.Model.Media; -using WatchIt.Database.Model.Rating; - -namespace WatchIt.Database.Model.Account -{ - public class Account : IEntity - { - #region PROPERTIES - - public long Id { get; set; } - public string Username { get; set; } - public string Email { get; set; } - public string? Description { get; set; } - public short? GenderId { get; set; } - public Guid? ProfilePictureId { get; set; } - public Guid? BackgroundPictureId { get; set; } - public byte[] Password { get; set; } - public string LeftSalt { get; set; } - public string RightSalt { get; set; } - public bool IsAdmin { get; set; } = false; - public DateTime CreationDate { get; set; } - public DateTime LastActive { get; set; } - - #endregion - - - - #region NAVIGATION - - public Gender Gender { get; set; } - public AccountProfilePicture? ProfilePicture { get; set; } - public MediaPhotoImage? BackgroundPicture { get; set; } - - public IEnumerable RatingMedia { get; set; } - public IEnumerable RatingPersonActorRole { get; set; } - public IEnumerable RatingPersonCreatorRole { get; set; } - public IEnumerable RatingMediaSeriesSeason { get; set; } - public IEnumerable RatingMediaSeriesEpisode { get; set; } - - public IEnumerable AccountRefreshTokens { get; set; } - - #endregion - - - - #region METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Username) - .HasMaxLength(50) - .IsRequired(); - - builder.Property(x => x.Email) - .HasMaxLength(320) - .IsRequired(); - - builder.Property(x => x.Description) - .HasMaxLength(1000); - - builder.HasOne(x => x.Gender) - .WithMany() - .HasForeignKey(x => x.GenderId); - builder.Property(x => x.GenderId); - - builder.HasOne(x => x.ProfilePicture) - .WithOne(x => x.Account) - .HasForeignKey(e => e.ProfilePictureId); - builder.Property(x => x.ProfilePictureId); - - builder.HasOne(x => x.BackgroundPicture) - .WithMany() - .HasForeignKey(x => x.BackgroundPictureId); - builder.Property(x => x.BackgroundPictureId); - - builder.Property(x => x.Password) - .HasMaxLength(1000) - .IsRequired(); - - builder.Property(x => x.LeftSalt) - .HasMaxLength(20) - .IsRequired(); - - builder.Property(x => x.RightSalt) - .HasMaxLength(20) - .IsRequired(); - - builder.Property(x => x.IsAdmin) - .IsRequired() - .HasDefaultValue(false); - - builder.Property(x => x.CreationDate) - .IsRequired() - .HasDefaultValueSql("now()"); - - builder.Property(x => x.LastActive) - .IsRequired() - .HasDefaultValueSql("now()"); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Account/AccountProfilePicture.cs b/WatchIt.Database/WatchIt.Database.Model/Account/AccountProfilePicture.cs deleted file mode 100644 index 4a7644d..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Account/AccountProfilePicture.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Account -{ - public class AccountProfilePicture : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public byte[] Image { get; set; } - public string MimeType { get; set; } - public DateTime UploadDate { get; set; } - - #endregion - - - - #region NAVIGATION - - public Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Image) - .HasMaxLength(-1) - .IsRequired(); - - builder.Property(x => x.MimeType) - .HasMaxLength(50) - .IsRequired(); - - builder.Property(x => x.UploadDate) - .IsRequired() - .HasDefaultValueSql("now()"); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs b/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs deleted file mode 100644 index 92aac22..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Account/AccountRefreshToken.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Account -{ - public class AccountRefreshToken : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long AccountId { get; set; } - public DateTime ExpirationDate { get; set; } - public bool IsExtendable { get; set; } - - #endregion - - - - #region NAVIGATION - - public Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.AccountRefreshTokens) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.ExpirationDate) - .IsRequired(); - - builder.Property(x => x.IsExtendable) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Common/Country.cs b/WatchIt.Database/WatchIt.Database.Model/Common/Country.cs deleted file mode 100644 index 954e917..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Common/Country.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.DataSeeding; -using WatchIt.Database.Model.Media; - -namespace WatchIt.Database.Model.Common -{ - public class Country : IEntity - { - #region PROPERTIES - - public short Id { get; set; } - public string Name { get; set; } - - #endregion - - - - #region NAVIGATION - - public IEnumerable MediaProductionCountries { get; set; } - public IEnumerable MediaProduction { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .HasMaxLength(100) - .IsRequired(); - - // Navigation - builder.HasMany(x => x.MediaProduction) - .WithMany(x => x.ProductionCountries) - .UsingEntity(); - } - - static IEnumerable IEntity.InsertData() => DataReader.Read(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Common/Gender.cs b/WatchIt.Database/WatchIt.Database.Model/Common/Gender.cs deleted file mode 100644 index c886c71..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Common/Gender.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.DataSeeding; -using WatchIt.Database.Model.Person; - -namespace WatchIt.Database.Model.Common -{ - public class Gender : IEntity - { - #region PROPERTIES - - public short Id { get; set; } - public string Name { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .IsRequired(); - } - - static IEnumerable IEntity.InsertData() => DataReader.Read(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Common/Genre.cs b/WatchIt.Database/WatchIt.Database.Model/Common/Genre.cs deleted file mode 100644 index bdf79f9..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Common/Genre.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.DataSeeding; -using WatchIt.Database.Model.Media; - -namespace WatchIt.Database.Model.Common -{ - public class Genre : IEntity - { - #region PROPERTIES - - public short Id { get; set; } - public string Name { get; set; } - public string? Description { get; set; } - - #endregion - - - - #region NAVIGATION - - public IEnumerable MediaGenres { get; set; } - public IEnumerable Media { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .HasMaxLength(100) - .IsRequired(); - - builder.Property(x => x.Description) - .HasMaxLength(1000); - - // Navigation - builder.HasMany(x => x.Media) - .WithMany(x => x.Genres) - .UsingEntity(); - } - - static IEnumerable IEntity.InsertData() => DataReader.Read(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/EntityBuilder.cs b/WatchIt.Database/WatchIt.Database.Model/EntityBuilder.cs deleted file mode 100644 index b1f7c24..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/EntityBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model -{ - public static class EntityBuilder - { - #region PUBLIC METHODS - - public static void Build(ModelBuilder builder) where T : class, IEntity => Build(builder.Entity()); - public static void Build(EntityTypeBuilder builder) where T : class, IEntity - { - T.Build(builder); - builder.HasData(T.InsertData()); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/IEntity.cs b/WatchIt.Database/WatchIt.Database.Model/IEntity.cs deleted file mode 100644 index ff6afc1..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/IEntity.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model -{ - public interface IEntity where T : class - { - #region METHODS - - static abstract void Build(EntityTypeBuilder builder); - static virtual IEnumerable InsertData() => Array.Empty(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/Media.cs b/WatchIt.Database/WatchIt.Database.Model/Media/Media.cs deleted file mode 100644 index bd6f0eb..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/Media.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Account; -using WatchIt.Database.Model.Common; -using WatchIt.Database.Model.Person; -using WatchIt.Database.Model.Rating; -using WatchIt.Database.Model.ViewCount; - -namespace WatchIt.Database.Model.Media -{ - public class Media : IEntity - { - #region PROPERTIES - - public long Id { get; set; } - public string Title { get; set; } - public string? OriginalTitle { get; set; } - public string? Description { get; set; } - public DateTime? ReleaseDate { get; set; } - public TimeSpan? Length { get; set; } - public Guid? MediaPosterImageId { get; set; } - - #endregion - - - - #region NAVIGATION - - public MediaPosterImage? MediaPosterImage { get; set; } - - public IEnumerable MediaPhotoImages { get; set; } - - public IEnumerable MediaGenres { get; set; } - public IEnumerable Genres { get; set; } - - public IEnumerable MediaProductionCountries { get; set; } - public IEnumerable ProductionCountries { get; set; } - - public IEnumerable PersonActorRoles { get; set; } - - public IEnumerable PersonCreatorRoles { get; set; } - - public IEnumerable RatingMedia { get; set; } - - public IEnumerable ViewCountsMedia { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Title) - .HasMaxLength(250) - .IsRequired(); - - builder.Property(x => x.OriginalTitle) - .HasMaxLength(250); - - builder.Property(x => x.Description) - .HasMaxLength(1000); - - builder.Property(x => x.ReleaseDate); - - builder.Property(x => x.Length); - - builder.HasOne(x => x.MediaPosterImage) - .WithOne(x => x.Media) - .HasForeignKey(x => x.MediaPosterImageId); - builder.Property(x => x.MediaPosterImageId); - - // Navigation - builder.HasMany(x => x.Genres) - .WithMany(x => x.Media) - .UsingEntity(); - builder.HasMany(x => x.ProductionCountries) - .WithMany(x => x.MediaProduction) - .UsingEntity(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaGenre.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaGenre.cs deleted file mode 100644 index e331fdf..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaGenre.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Common; - -namespace WatchIt.Database.Model.Media -{ - public class MediaGenre : IEntity - { - #region PROPERTIES - - public long MediaId { get; set; } - public short GenreId { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - public Genre Genre { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasOne(x => x.Media) - .WithMany(x => x.MediaGenres) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.HasOne(x => x.Genre) - .WithMany(x => x.MediaGenres) - .HasForeignKey(x => x.GenreId) - .IsRequired(); - builder.Property(x => x.GenreId) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaMovie.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaMovie.cs deleted file mode 100644 index 543a2e4..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaMovie.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Media -{ - public class MediaMovie : IEntity - { - #region PROPERTIES - - public long Id { get; set; } - public decimal? Budget { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasOne(x => x.Media) - .WithOne() - .HasForeignKey(x => x.Id) - .IsRequired(); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Budget) - .HasColumnType("money"); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaPhotoImage.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaPhotoImage.cs deleted file mode 100644 index b5e17e2..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaPhotoImage.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Media -{ - public class MediaPhotoImage : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long MediaId { get; set; } - public byte[] Image { get; set; } - public string MimeType { get; set; } - public DateTime UploadDate { get; set; } - public bool IsMediaBackground { get; set; } - public bool IsUniversalBackground { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Media) - .WithMany(x => x.MediaPhotoImages) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.Property(x => x.Image) - .HasMaxLength(-1) - .IsRequired(); - - builder.Property(x => x.MimeType) - .HasMaxLength(50) - .IsRequired(); - - builder.Property(x => x.UploadDate) - .IsRequired() - .HasDefaultValueSql("now()"); - - builder.Property(x => x.IsMediaBackground) - .IsRequired() - .HasDefaultValue(false); - - builder.Property(x => x.IsUniversalBackground) - .IsRequired() - .HasDefaultValue(false); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaPosterImage.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaPosterImage.cs deleted file mode 100644 index 76cc1ba..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaPosterImage.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Media -{ - public class MediaPosterImage : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public byte[] Image { get; set; } - public string MimeType { get; set; } - public DateTime UploadDate { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Image) - .HasMaxLength(-1) - .IsRequired(); - - builder.Property(x => x.MimeType) - .HasMaxLength(50) - .IsRequired(); - - builder.Property(x => x.UploadDate) - .IsRequired() - .HasDefaultValueSql("now()"); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaProductionCountry.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaProductionCountry.cs deleted file mode 100644 index 0e2de78..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaProductionCountry.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Common; - -namespace WatchIt.Database.Model.Media -{ - public class MediaProductionCountry : IEntity - { - #region PROPERTIES - - public long MediaId { get; set; } - public short CountryId { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - public Country Country { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasOne(x => x.Media) - .WithMany(x => x.MediaProductionCountries) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.HasOne(x => x.Country) - .WithMany(x => x.MediaProductionCountries) - .HasForeignKey(x => x.CountryId) - .IsRequired(); - builder.Property(x => x.CountryId) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeries.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeries.cs deleted file mode 100644 index 04a620d..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeries.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Media -{ - public class MediaSeries : IEntity - { - #region PROPERTIES - - public long Id { get; set; } - public bool HasEnded { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media Media { get; set; } - public IEnumerable MediaSeriesSeasons { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasOne(x => x.Media) - .WithOne() - .HasForeignKey(x => x.Id) - .IsRequired(); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.HasEnded); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs deleted file mode 100644 index 4dc361a..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Rating; - -namespace WatchIt.Database.Model.Media -{ - public class MediaSeriesEpisode : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public Guid MediaSeriesSeasonId { get; set; } - public short Number { get; set; } - public string? Name { get; set; } - public bool IsSpecial { get; set; } - - #endregion - - - - #region NAVIGATION - - public MediaSeriesSeason MediaSeriesSeason { get; set; } - public IEnumerable RatingMediaSeriesEpisode { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.MediaSeriesSeason) - .WithMany(x => x.MediaSeriesEpisodes) - .HasForeignKey(x => x.MediaSeriesSeasonId) - .IsRequired(); - builder.Property(x => x.MediaSeriesSeasonId) - .IsRequired(); - - builder.Property(x => x.Number) - .IsRequired(); - - builder.Property(x => x.Name); - - builder.Property(x => x.IsSpecial) - .IsRequired() - .HasDefaultValue(false); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesSeason.cs b/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesSeason.cs deleted file mode 100644 index 6ed841c..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Media/MediaSeriesSeason.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Rating; - -namespace WatchIt.Database.Model.Media -{ - public class MediaSeriesSeason : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long MediaSeriesId { get; set; } - public short Number { get; set; } - public string? Name { get; set; } - - #endregion - - - - #region NAVIGATION - - public MediaSeries MediaSeries { get; set; } - public IEnumerable MediaSeriesEpisodes { get; set; } - public IEnumerable RatingMediaSeriesSeason { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.MediaSeries) - .WithMany(x => x.MediaSeriesSeasons) - .HasForeignKey(x => x.MediaSeriesId) - .IsRequired(); - builder.Property(x => x.MediaSeriesId) - .IsRequired(); - - builder.Property(x => x.Number) - .IsRequired(); - - builder.Property(x => x.Name); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/Person.cs b/WatchIt.Database/WatchIt.Database.Model/Person/Person.cs deleted file mode 100644 index 8b8d456..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/Person.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Account; -using WatchIt.Database.Model.Common; -using WatchIt.Database.Model.ViewCount; - -namespace WatchIt.Database.Model.Person -{ - public class Person : IEntity - { - #region PROPERTIES - - public long Id { get; set; } - public string Name { get; set; } - public string? FullName { get; set; } - public string? Description { get; set; } - public DateOnly? BirthDate { get; set; } - public DateOnly? DeathDate { get; set; } - public short GenderId { get; set; } - public Guid? PersonPhotoId { get; set; } - - #endregion - - - - #region NAVIGATION - - public Gender Gender { get; set; } - public PersonPhotoImage? PersonPhoto { get; set; } - public IEnumerable PersonActorRoles { get; set; } - public IEnumerable PersonCreatorRoles { get; set; } - public IEnumerable ViewCountsPerson { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .HasMaxLength(100) - .IsRequired(); - - builder.Property(x => x.FullName) - .HasMaxLength(200); - - builder.Property(x => x.Description) - .HasMaxLength(1000); - - builder.Property(x => x.BirthDate); - - builder.Property(x => x.DeathDate); - - builder.HasOne(x => x.Gender) - .WithMany() - .HasForeignKey(x => x.GenderId) - .IsRequired(); - builder.Property(x => x.GenderId) - .IsRequired(); - - builder.HasOne(x => x.PersonPhoto) - .WithOne(x => x.Person) - .HasForeignKey(e => e.PersonPhotoId); - builder.Property(x => x.PersonPhotoId); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRole.cs b/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRole.cs deleted file mode 100644 index 815a9bc..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRole.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Media; -using WatchIt.Database.Model.Rating; - -namespace WatchIt.Database.Model.Person -{ - public class PersonActorRole : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long PersonId { get; set; } - public long MediaId { get; set; } - public short PersonActorRoleTypeId { get; set; } - public string RoleName { get; set; } - - #endregion - - - - #region NAVIGATION - - public Person Person { get; set; } - public Media.Media Media { get; set; } - public PersonActorRoleType PersonActorRoleType { get; set; } - - public IEnumerable RatingPersonActorRole { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Person) - .WithMany(x => x.PersonActorRoles) - .HasForeignKey(x => x.PersonId) - .IsRequired(); - builder.Property(x => x.PersonId) - .IsRequired(); - - builder.HasOne(x => x.Media) - .WithMany(x => x.PersonActorRoles) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.HasOne(x => x.PersonActorRoleType) - .WithMany() - .HasForeignKey(x => x.PersonActorRoleTypeId) - .IsRequired(); - builder.Property(x => x.PersonActorRoleTypeId) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRoleType.cs b/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRoleType.cs deleted file mode 100644 index 06a91a3..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/PersonActorRoleType.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.DataSeeding; - -namespace WatchIt.Database.Model.Person -{ - public class PersonActorRoleType : IEntity - { - #region PROPERTIES - - public short Id { get; set; } - public string Name { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .HasMaxLength(100) - .IsRequired(); - } - - static IEnumerable IEntity.InsertData() => DataReader.Read(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRole.cs b/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRole.cs deleted file mode 100644 index 84bf2a1..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRole.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Rating; - -namespace WatchIt.Database.Model.Person -{ - public class PersonCreatorRole : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long PersonId { get; set; } - public long MediaId { get; set; } - public short PersonCreatorRoleTypeId { get; set; } - - #endregion - - - - #region NAVIGATION - - public Person Person { get; set; } - public Media.Media Media { get; set; } - public PersonCreatorRoleType PersonCreatorRoleType { get; set; } - public IEnumerable RatingPersonCreatorRole { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Person) - .WithMany(x => x.PersonCreatorRoles) - .HasForeignKey(x => x.PersonId) - .IsRequired(); - builder.Property(x => x.PersonId) - .IsRequired(); - - builder.HasOne(x => x.Media) - .WithMany(x => x.PersonCreatorRoles) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.HasOne(x => x.PersonCreatorRoleType) - .WithMany() - .HasForeignKey(x => x.PersonCreatorRoleTypeId) - .IsRequired(); - builder.Property(x => x.PersonCreatorRoleTypeId) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs b/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs deleted file mode 100644 index 03a1b93..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.DataSeeding; - -namespace WatchIt.Database.Model.Person -{ - public class PersonCreatorRoleType : IEntity - { - #region PROPERTIES - - public short Id { get; set; } - public string Name { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Name) - .HasMaxLength(100) - .IsRequired(); - } - - static IEnumerable IEntity.InsertData() => DataReader.Read(); - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Person/PersonPhotoImage.cs b/WatchIt.Database/WatchIt.Database.Model/Person/PersonPhotoImage.cs deleted file mode 100644 index f976b36..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Person/PersonPhotoImage.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Account; - -namespace WatchIt.Database.Model.Person -{ - public class PersonPhotoImage : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public byte[] Image { get; set; } - public string MimeType { get; set; } - public DateTime UploadDate { get; set; } - - #endregion - - - - #region NAVIGATION - - public Person Person { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.Property(x => x.Image) - .HasMaxLength(-1) - .IsRequired(); - - builder.Property(x => x.MimeType) - .HasMaxLength(50) - .IsRequired(); - - builder.Property(x => x.UploadDate) - .IsRequired() - .HasDefaultValueSql("now()"); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMedia.cs b/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMedia.cs deleted file mode 100644 index a293f84..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMedia.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.Rating -{ - public class RatingMedia : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long MediaId { get; set; } - public long AccountId { get; set; } - public short Rating { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media.Media Media { get; set; } - public Account.Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Media) - .WithMany(x => x.RatingMedia) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.RatingMedia) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.Rating) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs b/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs deleted file mode 100644 index c56bce0..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Media; - -namespace WatchIt.Database.Model.Rating -{ - public class RatingMediaSeriesEpisode : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public Guid MediaSeriesEpisodeId { get; set; } - public long AccountId { get; set; } - public short Rating { get; set; } - - #endregion - - - - #region NAVIGATION - - public MediaSeriesEpisode MediaSeriesEpisode { get; set; } - public Account.Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.MediaSeriesEpisode) - .WithMany(x => x.RatingMediaSeriesEpisode) - .HasForeignKey(x => x.MediaSeriesEpisodeId) - .IsRequired(); - builder.Property(x => x.MediaSeriesEpisodeId) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.RatingMediaSeriesEpisode) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.Rating) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs b/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs deleted file mode 100644 index 42dcf3e..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Media; -using WatchIt.Database.Model.Person; - -namespace WatchIt.Database.Model.Rating -{ - public class RatingMediaSeriesSeason : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public Guid MediaSeriesSeasonId { get; set; } - public long AccountId { get; set; } - public short Rating { get; set; } - - #endregion - - - - #region NAVIGATION - - public MediaSeriesSeason MediaSeriesSeason { get; set; } - public Account.Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.MediaSeriesSeason) - .WithMany(x => x.RatingMediaSeriesSeason) - .HasForeignKey(x => x.MediaSeriesSeasonId) - .IsRequired(); - builder.Property(x => x.MediaSeriesSeasonId) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.RatingMediaSeriesSeason) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.Rating) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs b/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs deleted file mode 100644 index f68118e..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Person; - -namespace WatchIt.Database.Model.Rating -{ - public class RatingPersonActorRole : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public Guid PersonActorRoleId { get; set; } - public long AccountId { get; set; } - public short Rating { get; set; } - - #endregion - - - - #region NAVIGATION - - public PersonActorRole PersonActorRole { get; set; } - public Account.Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.ToTable("RatingsPersonActorRole"); - - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.PersonActorRole) - .WithMany(x => x.RatingPersonActorRole) - .HasForeignKey(x => x.PersonActorRoleId) - .IsRequired(); - builder.Property(x => x.PersonActorRoleId) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.RatingPersonActorRole) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.Rating) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs b/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs deleted file mode 100644 index ae82b11..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model.Person; - -namespace WatchIt.Database.Model.Rating -{ - public class RatingPersonCreatorRole : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public Guid PersonCreatorRoleId { get; set; } - public long AccountId { get; set; } - public short Rating { get; set; } - - #endregion - - - - #region NAVIGATION - - public PersonCreatorRole PersonCreatorRole { get; set; } - public Account.Account Account { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.ToTable("RatingsPersonCreatorRole"); - - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.PersonCreatorRole) - .WithMany(x => x.RatingPersonCreatorRole) - .HasForeignKey(x => x.PersonCreatorRoleId) - .IsRequired(); - builder.Property(x => x.PersonCreatorRoleId) - .IsRequired(); - - builder.HasOne(x => x.Account) - .WithMany(x => x.RatingPersonCreatorRole) - .HasForeignKey(x => x.AccountId) - .IsRequired(); - builder.Property(x => x.AccountId) - .IsRequired(); - - builder.Property(x => x.Rating) - .IsRequired(); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs b/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs deleted file mode 100644 index 9281fc1..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.ViewCount -{ - public class ViewCountMedia : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long MediaId { get; set; } - public DateOnly Date { get; set; } - public long ViewCount { get; set; } - - #endregion - - - - #region NAVIGATION - - public Media.Media Media { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.ToTable("ViewCountsMedia"); - - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Media) - .WithMany(x => x.ViewCountsMedia) - .HasForeignKey(x => x.MediaId) - .IsRequired(); - builder.Property(x => x.MediaId) - .IsRequired(); - - builder.Property(x => x.Date) - .IsRequired() - .HasDefaultValueSql("now()"); - - builder.Property(x => x.ViewCount) - .IsRequired() - .HasDefaultValue(0); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs b/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs deleted file mode 100644 index c97336b..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Database.Model.ViewCount -{ - public class ViewCountPerson : IEntity - { - #region PROPERTIES - - public Guid Id { get; set; } - public long PersonId { get; set; } - public DateOnly Date { get; set; } - public long ViewCount { get; set; } - - #endregion - - - - #region NAVIGATION - - public Person.Person Person { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - static void IEntity.Build(EntityTypeBuilder builder) - { - builder.ToTable("ViewCountsPerson"); - - builder.HasKey(x => x.Id); - builder.HasIndex(x => x.Id) - .IsUnique(); - builder.Property(x => x.Id) - .IsRequired(); - - builder.HasOne(x => x.Person) - .WithMany(x => x.ViewCountsPerson) - .HasForeignKey(x => x.PersonId) - .IsRequired(); - builder.Property(x => x.PersonId) - .IsRequired(); - - builder.Property(x => x.Date) - .IsRequired() - .HasDefaultValueSql("now()"); - - builder.Property(x => x.ViewCount) - .IsRequired() - .HasDefaultValue(0); - } - - #endregion - } -} diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountConfiguration.cs new file mode 100644 index 0000000..5687c3a --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountConfiguration.cs @@ -0,0 +1,66 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace WatchIt.Database.Model.Configuration.Account; + +public class AccountConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Username) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.Email) + .HasMaxLength(320) + .IsRequired(); + + builder.Property(x => x.Description) + .HasMaxLength(1000); + + builder.HasOne(x => x.Gender) + .WithMany() + .HasForeignKey(x => x.GenderId); + builder.Property(x => x.GenderId); + + builder.HasOne(x => x.ProfilePicture) + .WithOne(x => x.Account) + .HasForeignKey(e => e.ProfilePictureId); + builder.Property(x => x.ProfilePictureId); + + builder.HasOne(x => x.BackgroundPicture) + .WithMany() + .HasForeignKey(x => x.BackgroundPictureId); + builder.Property(x => x.BackgroundPictureId); + + builder.Property(x => x.Password) + .HasMaxLength(1000) + .IsRequired(); + + builder.Property(x => x.LeftSalt) + .HasMaxLength(20) + .IsRequired(); + + builder.Property(x => x.RightSalt) + .HasMaxLength(20) + .IsRequired(); + + builder.Property(x => x.IsAdmin) + .IsRequired() + .HasDefaultValue(false); + + builder.Property(x => x.CreationDate) + .IsRequired() + .HasDefaultValueSql("now()"); + + builder.Property(x => x.LastActive) + .IsRequired() + .HasDefaultValueSql("now()"); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountProfilePictureConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountProfilePictureConfiguration.cs new file mode 100644 index 0000000..1950a35 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountProfilePictureConfiguration.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Account; + +namespace WatchIt.Database.Model.Configuration.Account; + +public class AccountProfilePictureConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Image) + .HasMaxLength(-1) + .IsRequired(); + + builder.Property(x => x.MimeType) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.UploadDate) + .IsRequired() + .HasDefaultValueSql("now()"); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountRefreshTokenConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountRefreshTokenConfiguration.cs new file mode 100644 index 0000000..f0e9e19 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Account/AccountRefreshTokenConfiguration.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Account; + +namespace WatchIt.Database.Model.Configuration.Account; + +public class AccountRefreshTokenConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.AccountRefreshTokens) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.ExpirationDate) + .IsRequired(); + + builder.Property(x => x.IsExtendable) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/CountryConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/CountryConfiguration.cs new file mode 100644 index 0000000..64d4292 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/CountryConfiguration.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Media; +using WatchIt.Database.Model.Seeding; + +namespace WatchIt.Database.Model.Configuration.Common; + +public class CountryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + builder.Property(x => x.IsHistorical) + .HasDefaultValue(false) + .IsRequired(); + + // Navigation + builder.HasMany(x => x.MediaProduction) + .WithMany(x => x.ProductionCountries) + .UsingEntity(); + + // Data + builder.HasData(DataReader.Read()); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenderConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenderConfiguration.cs new file mode 100644 index 0000000..1133568 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenderConfiguration.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Seeding; + +namespace WatchIt.Database.Model.Configuration.Common; + +public class GenderConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .IsRequired(); + + // Data + builder.HasData(DataReader.Read()); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenreConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenreConfiguration.cs new file mode 100644 index 0000000..4b4b665 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Common/GenreConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Media; +using WatchIt.Database.Model.Seeding; + +namespace WatchIt.Database.Model.Configuration.Common; + +public class GenreConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + builder.Property(x => x.Description) + .HasMaxLength(1000); + + // Navigation + builder.HasMany(x => x.Media) + .WithMany(x => x.Genres) + .UsingEntity(); + + // Data + builder.HasData(DataReader.Read()); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaConfiguration.cs new file mode 100644 index 0000000..91af102 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaConfiguration.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Title) + .HasMaxLength(250) + .IsRequired(); + + builder.Property(x => x.OriginalTitle) + .HasMaxLength(250); + + builder.Property(x => x.Description) + .HasMaxLength(1000); + + builder.Property(x => x.ReleaseDate); + + builder.Property(x => x.Length); + + builder.HasOne(x => x.MediaPosterImage) + .WithOne(x => x.Media) + .HasForeignKey(x => x.MediaPosterImageId); + builder.Property(x => x.MediaPosterImageId); + + // Navigation + builder.HasMany(x => x.Genres) + .WithMany(x => x.Media) + .UsingEntity(); + builder.HasMany(x => x.ProductionCountries) + .WithMany(x => x.MediaProduction) + .UsingEntity(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaGenreConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaGenreConfiguration.cs new file mode 100644 index 0000000..2c9deeb --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaGenreConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaGenreConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasOne(x => x.Media) + .WithMany(x => x.MediaGenres) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.HasOne(x => x.Genre) + .WithMany(x => x.MediaGenres) + .HasForeignKey(x => x.GenreId) + .IsRequired(); + builder.Property(x => x.GenreId) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaMovieConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaMovieConfiguration.cs new file mode 100644 index 0000000..4b4a2f0 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaMovieConfiguration.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaMovieConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasOne(x => x.Media) + .WithOne() + .HasForeignKey(x => x.Id) + .IsRequired(); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Budget) + .HasColumnType("money"); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPhotoImageConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPhotoImageConfiguration.cs new file mode 100644 index 0000000..c2476a1 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPhotoImageConfiguration.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaPhotoImageConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Media) + .WithMany(x => x.MediaPhotoImages) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.Property(x => x.Image) + .HasMaxLength(-1) + .IsRequired(); + + builder.Property(x => x.MimeType) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.UploadDate) + .IsRequired() + .HasDefaultValueSql("now()"); + + builder.Property(x => x.IsMediaBackground) + .IsRequired() + .HasDefaultValue(false); + + builder.Property(x => x.IsUniversalBackground) + .IsRequired() + .HasDefaultValue(false); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPosterImageConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPosterImageConfiguration.cs new file mode 100644 index 0000000..aad0529 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaPosterImageConfiguration.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaPosterImageConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Image) + .HasMaxLength(-1) + .IsRequired(); + + builder.Property(x => x.MimeType) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.UploadDate) + .IsRequired() + .HasDefaultValueSql("now()"); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaProductionCountryConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaProductionCountryConfiguration.cs new file mode 100644 index 0000000..effca7a --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaProductionCountryConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaProductionCountryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasOne(x => x.Media) + .WithMany(x => x.MediaProductionCountries) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.HasOne(x => x.Country) + .WithMany(x => x.MediaProductionCountries) + .HasForeignKey(x => x.CountryId) + .IsRequired(); + builder.Property(x => x.CountryId) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesConfiguration.cs new file mode 100644 index 0000000..966593b --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaSeriesConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasOne(x => x.Media) + .WithOne() + .HasForeignKey(x => x.Id) + .IsRequired(); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.HasEnded) + .IsRequired() + .HasDefaultValue(false); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesEpisodeConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesEpisodeConfiguration.cs new file mode 100644 index 0000000..7fd5d8d --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesEpisodeConfiguration.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaSeriesEpisodeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.MediaSeriesSeason) + .WithMany(x => x.MediaSeriesEpisodes) + .HasForeignKey(x => x.MediaSeriesSeasonId) + .IsRequired(); + builder.Property(x => x.MediaSeriesSeasonId) + .IsRequired(); + + builder.Property(x => x.Number) + .IsRequired(); + + builder.Property(x => x.Name); + + builder.Property(x => x.IsSpecial) + .IsRequired() + .HasDefaultValue(false); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesSeasonConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesSeasonConfiguration.cs new file mode 100644 index 0000000..3bf8dd4 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Media/MediaSeriesSeasonConfiguration.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Configuration.Media; + +public class MediaSeriesSeasonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.MediaSeries) + .WithMany(x => x.MediaSeriesSeasons) + .HasForeignKey(x => x.MediaSeriesId) + .IsRequired(); + builder.Property(x => x.MediaSeriesId) + .IsRequired(); + + builder.Property(x => x.Number) + .IsRequired(); + + builder.Property(x => x.Name); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleConfiguration.cs new file mode 100644 index 0000000..b94193e --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleConfiguration.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Person; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonActorRoleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Person) + .WithMany(x => x.PersonActorRoles) + .HasForeignKey(x => x.PersonId) + .IsRequired(); + builder.Property(x => x.PersonId) + .IsRequired(); + + builder.HasOne(x => x.Media) + .WithMany(x => x.PersonActorRoles) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.HasOne(x => x.PersonActorRoleType) + .WithMany() + .HasForeignKey(x => x.PersonActorRoleTypeId) + .IsRequired(); + builder.Property(x => x.PersonActorRoleTypeId) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleTypeConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleTypeConfiguration.cs new file mode 100644 index 0000000..bae2d0e --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonActorRoleTypeConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Person; +using WatchIt.Database.Model.Seeding; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonActorRoleTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + // Data + builder.HasData(DataReader.Read()); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonConfiguration.cs new file mode 100644 index 0000000..af5a767 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonConfiguration.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + builder.Property(x => x.FullName) + .HasMaxLength(200); + + builder.Property(x => x.Description) + .HasMaxLength(1000); + + builder.Property(x => x.BirthDate); + + builder.Property(x => x.DeathDate); + + builder.HasOne(x => x.Gender) + .WithMany() + .HasForeignKey(x => x.GenderId); + builder.Property(x => x.GenderId); + + builder.HasOne(x => x.PersonPhoto) + .WithOne(x => x.Person) + .HasForeignKey(e => e.PersonPhotoId); + builder.Property(x => x.PersonPhotoId); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleConfiguration.cs new file mode 100644 index 0000000..1c0e2a6 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleConfiguration.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Person; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonCreatorRoleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Person) + .WithMany(x => x.PersonCreatorRoles) + .HasForeignKey(x => x.PersonId) + .IsRequired(); + builder.Property(x => x.PersonId) + .IsRequired(); + + builder.HasOne(x => x.Media) + .WithMany(x => x.PersonCreatorRoles) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.HasOne(x => x.PersonCreatorRoleType) + .WithMany() + .HasForeignKey(x => x.PersonCreatorRoleTypeId) + .IsRequired(); + builder.Property(x => x.PersonCreatorRoleTypeId) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleTypeConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleTypeConfiguration.cs new file mode 100644 index 0000000..6d456ae --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonCreatorRoleTypeConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Person; +using WatchIt.Database.Model.Seeding; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonCreatorRoleTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + // Data + builder.HasData(DataReader.Read()); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonPhotoImageConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonPhotoImageConfiguration.cs new file mode 100644 index 0000000..2fe5f55 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Person/PersonPhotoImageConfiguration.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Person; + +namespace WatchIt.Database.Model.Configuration.Person; + +public class PersonPhotoImageConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.Property(x => x.Image) + .HasMaxLength(-1) + .IsRequired(); + + builder.Property(x => x.MimeType) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.UploadDate) + .IsRequired() + .HasDefaultValueSql("now()"); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaConfiguration.cs new file mode 100644 index 0000000..7791626 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Configuration.Rating; + +public class RatingMediaConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Media) + .WithMany(x => x.RatingMedia) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.RatingMedia) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.Rating) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesEpisodeConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesEpisodeConfiguration.cs new file mode 100644 index 0000000..df2e5b0 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesEpisodeConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Configuration.Rating; + +public class RatingMediaSeriesEpisodeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.MediaSeriesEpisode) + .WithMany(x => x.RatingMediaSeriesEpisode) + .HasForeignKey(x => x.MediaSeriesEpisodeId) + .IsRequired(); + builder.Property(x => x.MediaSeriesEpisodeId) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.RatingMediaSeriesEpisode) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.Rating) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesSeasonConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesSeasonConfiguration.cs new file mode 100644 index 0000000..b777a13 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingMediaSeriesSeasonConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Configuration.Rating; + +public class RatingMediaSeriesSeasonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.MediaSeriesSeason) + .WithMany(x => x.RatingMediaSeriesSeason) + .HasForeignKey(x => x.MediaSeriesSeasonId) + .IsRequired(); + builder.Property(x => x.MediaSeriesSeasonId) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.RatingMediaSeriesSeason) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.Rating) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonActorRoleConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonActorRoleConfiguration.cs new file mode 100644 index 0000000..835914d --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonActorRoleConfiguration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Configuration.Rating; + +public class RatingPersonActorRoleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("RatingsPersonActorRole"); + + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.PersonActorRole) + .WithMany(x => x.RatingPersonActorRole) + .HasForeignKey(x => x.PersonActorRoleId) + .IsRequired(); + builder.Property(x => x.PersonActorRoleId) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.RatingPersonActorRole) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.Rating) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonCreatorRoleConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonCreatorRoleConfiguration.cs new file mode 100644 index 0000000..8b871ba --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/Rating/RatingPersonCreatorRoleConfiguration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Configuration.Rating; + +public class RatingPersonCreatorRoleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("RatingsPersonCreatorRole"); + + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.PersonCreatorRole) + .WithMany(x => x.RatingPersonCreatorRole) + .HasForeignKey(x => x.PersonCreatorRoleId) + .IsRequired(); + builder.Property(x => x.PersonCreatorRoleId) + .IsRequired(); + + builder.HasOne(x => x.Account) + .WithMany(x => x.RatingPersonCreatorRole) + .HasForeignKey(x => x.AccountId) + .IsRequired(); + builder.Property(x => x.AccountId) + .IsRequired(); + + builder.Property(x => x.Rating) + .IsRequired(); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountMediaConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountMediaConfiguration.cs new file mode 100644 index 0000000..9a81c42 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountMediaConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.ViewCount; + +namespace WatchIt.Database.Model.Configuration.ViewCount; + +public class ViewCountMediaConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ViewCountsMedia"); + + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Media) + .WithMany(x => x.ViewCountsMedia) + .HasForeignKey(x => x.MediaId) + .IsRequired(); + builder.Property(x => x.MediaId) + .IsRequired(); + + builder.Property(x => x.Date) + .IsRequired() + .HasDefaultValueSql("now()"); + + builder.Property(x => x.ViewCount) + .IsRequired() + .HasDefaultValue(0); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountPersonConfiguration.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountPersonConfiguration.cs new file mode 100644 index 0000000..cc7767c --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/ViewCount/ViewCountPersonConfiguration.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using WatchIt.Database.Model.ViewCount; + +namespace WatchIt.Database.Model.Configuration.ViewCount; + +public class ViewCountPersonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ViewCountsPerson"); + + builder.HasKey(x => x.Id); + builder.HasIndex(x => x.Id) + .IsUnique(); + builder.Property(x => x.Id) + .IsRequired(); + + builder.HasOne(x => x.Person) + .WithMany(x => x.ViewCountsPerson) + .HasForeignKey(x => x.PersonId) + .IsRequired(); + builder.Property(x => x.PersonId) + .IsRequired(); + + builder.Property(x => x.Date) + .IsRequired() + .HasDefaultValueSql("now()"); + + builder.Property(x => x.ViewCount) + .IsRequired() + .HasDefaultValue(0); + } +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/WatchIt.Database.Model.Configuration.csproj b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/WatchIt.Database.Model.Configuration.csproj new file mode 100644 index 0000000..2c0e44b --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Configuration/WatchIt.Database.Model.Configuration.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Country.json b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Country.json new file mode 100644 index 0000000..dd0c82e --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Country.json @@ -0,0 +1,12 @@ +[ + { + "Id": 1, + "Name": "Afghanistan", + "IsHistorical": false + }, + { + "Id": 2, + "Name": "Albania", + "IsHistorical": false + } +] \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/Gender.json b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Gender.json similarity index 100% rename from WatchIt.Database/WatchIt.Database.DataSeeding/Data/Gender.json rename to WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Gender.json diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/Genre.json b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Genre.json similarity index 100% rename from WatchIt.Database/WatchIt.Database.DataSeeding/Data/Genre.json rename to WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/Genre.json diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/PersonActorRoleType.json b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/PersonActorRoleType.json similarity index 100% rename from WatchIt.Database/WatchIt.Database.DataSeeding/Data/PersonActorRoleType.json rename to WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/PersonActorRoleType.json diff --git a/WatchIt.Database/WatchIt.Database.DataSeeding/Data/PersonCreatorRoleType.json b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/PersonCreatorRoleType.json similarity index 100% rename from WatchIt.Database/WatchIt.Database.DataSeeding/Data/PersonCreatorRoleType.json rename to WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/Data/PersonCreatorRoleType.json diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/DataReader.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/DataReader.cs new file mode 100644 index 0000000..0c32cdf --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/DataReader.cs @@ -0,0 +1,23 @@ +using System.Text.Json; + +namespace WatchIt.Database.Model.Seeding; + +public class DataReader +{ + #region METHODS + + public static IEnumerable Read() => Read(typeof(T).Name); + public static IEnumerable Read(string filename) + { + string jsonFile = $@"..\..\WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.Seeding\Data\{filename}.json"; + string dataString = File.ReadAllText(jsonFile); + IEnumerable? data = JsonSerializer.Deserialize>(dataString); + if (data is null) + { + throw new JsonException(); + } + return data; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/WatchIt.Database.Model.Seeding.csproj b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/WatchIt.Database.Model.Seeding.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.Seeding/WatchIt.Database.Model.Seeding.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.csproj b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.csproj deleted file mode 100644 index a3fae4a..0000000 --- a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs new file mode 100644 index 0000000..c24a5ec --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs @@ -0,0 +1,44 @@ +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Media; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Account; + +public class Account +{ + #region PROPERTIES + + public long Id { get; set; } + public required string Username { get; set; } + public required string Email { get; set; } + public string? Description { get; set; } + public short? GenderId { get; set; } + public Guid? ProfilePictureId { get; set; } + public Guid? BackgroundPictureId { get; set; } + public required byte[] Password { get; set; } + public required string LeftSalt { get; set; } + public required string RightSalt { get; set; } + public bool IsAdmin { get; set; } = false; + public DateTime CreationDate { get; set; } + public DateTime LastActive { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Gender? Gender { get; set; } + public virtual AccountProfilePicture? ProfilePicture { get; set; } + public virtual MediaPhotoImage? BackgroundPicture { get; set; } + + public virtual IEnumerable RatingMedia { get; set; } = new List(); + public virtual IEnumerable RatingPersonActorRole { get; set; } = new List(); + public virtual IEnumerable RatingPersonCreatorRole { get; set; } = new List(); + public virtual IEnumerable RatingMediaSeriesSeason { get; set; } = new List(); + public virtual IEnumerable RatingMediaSeriesEpisode { get; set; } = new List(); + + public virtual IEnumerable AccountRefreshTokens { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountProfilePicture.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountProfilePicture.cs new file mode 100644 index 0000000..9020589 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountProfilePicture.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.Account; + +public class AccountProfilePicture +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required byte[] Image { get; set; } + public required string MimeType { get; set; } + public DateTime UploadDate { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountRefreshToken.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountRefreshToken.cs new file mode 100644 index 0000000..d6da151 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/AccountRefreshToken.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.Account; + +public class AccountRefreshToken +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long AccountId { get; set; } + public required DateTime ExpirationDate { get; set; } + public required bool IsExtendable { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Country.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Country.cs new file mode 100644 index 0000000..3e05213 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Country.cs @@ -0,0 +1,23 @@ +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Common; + +public class Country +{ + #region PROPERTIES + + public short Id { get; set; } + public required string Name { get; set; } + public bool IsHistorical { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual IEnumerable MediaProductionCountries { get; set; } = new List(); + public virtual IEnumerable MediaProduction { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Gender.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Gender.cs new file mode 100644 index 0000000..afcffe6 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Gender.cs @@ -0,0 +1,11 @@ +namespace WatchIt.Database.Model.Common; + +public class Gender +{ + #region PROPERTIES + + public short Id { get; set; } + public required string Name { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Genre.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Genre.cs new file mode 100644 index 0000000..5d3c63b --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Common/Genre.cs @@ -0,0 +1,23 @@ +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Common; + +public class Genre +{ + #region PROPERTIES + + public short Id { get; set; } + public required string Name { get; set; } + public string? Description { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual IEnumerable MediaGenres { get; set; } = new List(); + public virtual IEnumerable Media { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/Media.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/Media.cs new file mode 100644 index 0000000..911d39e --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/Media.cs @@ -0,0 +1,45 @@ +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Person; +using WatchIt.Database.Model.Rating; +using WatchIt.Database.Model.ViewCount; + +namespace WatchIt.Database.Model.Media; + +public class Media +{ + #region PROPERTIES + + public long Id { get; set; } + public required string Title { get; set; } + public string? OriginalTitle { get; set; } + public string? Description { get; set; } + public DateOnly? ReleaseDate { get; set; } + public short? Length { get; set; } + public Guid? MediaPosterImageId { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual MediaPosterImage? MediaPosterImage { get; set; } + + public virtual IEnumerable MediaPhotoImages { get; set; } = new List(); + + public virtual IEnumerable MediaGenres { get; set; } = new List(); + public virtual IEnumerable Genres { get; set; } = new List(); + + public virtual IEnumerable MediaProductionCountries { get; set; } = new List(); + public virtual IEnumerable ProductionCountries { get; set; } = new List(); + + public virtual IEnumerable PersonActorRoles { get; set; } = new List(); + + public virtual IEnumerable PersonCreatorRoles { get; set; } = new List(); + + public virtual IEnumerable RatingMedia { get; set; } = new List(); + + public virtual IEnumerable ViewCountsMedia { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaGenre.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaGenre.cs new file mode 100644 index 0000000..a81e0c3 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaGenre.cs @@ -0,0 +1,22 @@ +using WatchIt.Database.Model.Common; + +namespace WatchIt.Database.Model.Media; + +public class MediaGenre +{ + #region PROPERTIES + + public required long MediaId { get; set; } + public required short GenreId { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + public virtual Genre Genre { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaMovie.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaMovie.cs new file mode 100644 index 0000000..b90f0d9 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaMovie.cs @@ -0,0 +1,19 @@ +namespace WatchIt.Database.Model.Media; + +public class MediaMovie +{ + #region PROPERTIES + + public long Id { get; set; } + public decimal? Budget { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPhotoImage.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPhotoImage.cs new file mode 100644 index 0000000..7c529b1 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPhotoImage.cs @@ -0,0 +1,24 @@ +namespace WatchIt.Database.Model.Media; + +public class MediaPhotoImage +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long MediaId { get; set; } + public required byte[] Image { get; set; } + public required string MimeType { get; set; } + public DateTime UploadDate { get; set; } + public bool IsMediaBackground { get; set; } + public bool IsUniversalBackground { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPosterImage.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPosterImage.cs new file mode 100644 index 0000000..c52c908 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaPosterImage.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.Media; + +public class MediaPosterImage +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required byte[] Image { get; set; } + public required string MimeType { get; set; } + public required DateTime UploadDate { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaProductionCountry.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaProductionCountry.cs new file mode 100644 index 0000000..715e338 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaProductionCountry.cs @@ -0,0 +1,22 @@ +using WatchIt.Database.Model.Common; + +namespace WatchIt.Database.Model.Media; + +public class MediaProductionCountry +{ + #region PROPERTIES + + public required long MediaId { get; set; } + public required short CountryId { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + public virtual Country Country { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeries.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeries.cs new file mode 100644 index 0000000..9f6eb8e --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeries.cs @@ -0,0 +1,20 @@ +namespace WatchIt.Database.Model.Media; + +public class MediaSeries +{ + #region PROPERTIES + + public required long Id { get; set; } + public bool HasEnded { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media Media { get; set; } = null!; + public virtual IEnumerable MediaSeriesSeasons { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs new file mode 100644 index 0000000..6b87631 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesEpisode.cs @@ -0,0 +1,25 @@ +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Media; + +public class MediaSeriesEpisode +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required Guid MediaSeriesSeasonId { get; set; } + public required short Number { get; set; } + public string? Name { get; set; } + public bool IsSpecial { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual MediaSeriesSeason MediaSeriesSeason { get; set; } = null!; + public virtual IEnumerable RatingMediaSeriesEpisode { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesSeason.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesSeason.cs new file mode 100644 index 0000000..53e8a6f --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Media/MediaSeriesSeason.cs @@ -0,0 +1,25 @@ +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Media; + +public class MediaSeriesSeason +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long MediaSeriesId { get; set; } + public required short Number { get; set; } + public string? Name { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual MediaSeries MediaSeries { get; set; } = null!; + public virtual IEnumerable MediaSeriesEpisodes { get; set; } = new List(); + public virtual IEnumerable RatingMediaSeriesSeason { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/Person.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/Person.cs new file mode 100644 index 0000000..cccbe17 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/Person.cs @@ -0,0 +1,32 @@ +using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.ViewCount; + +namespace WatchIt.Database.Model.Person; + +public class Person +{ + #region PROPERTIES + + public long Id { get; set; } + public required string Name { get; set; } + public string? FullName { get; set; } + public string? Description { get; set; } + public DateOnly? BirthDate { get; set; } + public DateOnly? DeathDate { get; set; } + public short? GenderId { get; set; } + public Guid? PersonPhotoId { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Gender? Gender { get; set; } + public virtual PersonPhotoImage? PersonPhoto { get; set; } + public virtual IEnumerable PersonActorRoles { get; set; } = new List(); + public virtual IEnumerable PersonCreatorRoles { get; set; } = new List(); + public virtual IEnumerable ViewCountsPerson { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRole.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRole.cs new file mode 100644 index 0000000..659236f --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRole.cs @@ -0,0 +1,28 @@ +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Person; + +public class PersonActorRole +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long PersonId { get; set; } + public required long MediaId { get; set; } + public required short PersonActorRoleTypeId { get; set; } + public required string RoleName { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Person Person { get; set; } = null!; + public virtual Media.Media Media { get; set; } = null!; + public virtual PersonActorRoleType PersonActorRoleType { get; set; } = null!; + + public virtual IEnumerable RatingPersonActorRole { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRoleType.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRoleType.cs new file mode 100644 index 0000000..2210a6f --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonActorRoleType.cs @@ -0,0 +1,11 @@ +namespace WatchIt.Database.Model.Person; + +public class PersonActorRoleType +{ + #region PROPERTIES + + public short Id { get; set; } + public required string Name { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRole.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRole.cs new file mode 100644 index 0000000..17a240d --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRole.cs @@ -0,0 +1,26 @@ +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Database.Model.Person; + +public class PersonCreatorRole +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long PersonId { get; set; } + public required long MediaId { get; set; } + public required short PersonCreatorRoleTypeId { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Person Person { get; set; } = null!; + public virtual Media.Media Media { get; set; } = null!; + public virtual PersonCreatorRoleType PersonCreatorRoleType { get; set; } = null!; + public virtual IEnumerable RatingPersonCreatorRole { get; set; } = new List(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs new file mode 100644 index 0000000..fb2ef60 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonCreatorRoleType.cs @@ -0,0 +1,11 @@ +namespace WatchIt.Database.Model.Person; + +public class PersonCreatorRoleType +{ + #region PROPERTIES + + public short Id { get; set; } + public required string Name { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonPhotoImage.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonPhotoImage.cs new file mode 100644 index 0000000..ce843f0 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Person/PersonPhotoImage.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.Person; + +public class PersonPhotoImage +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required byte[] Image { get; set; } + public required string MimeType { get; set; } + public DateTime UploadDate { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Person Person { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMedia.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMedia.cs new file mode 100644 index 0000000..f4e3b67 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMedia.cs @@ -0,0 +1,22 @@ +namespace WatchIt.Database.Model.Rating; + +public class RatingMedia +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long MediaId { get; set; } + public required long AccountId { get; set; } + public required short Rating { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Model.Media.Media Media { get; set; } = null!; + public virtual Model.Account.Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs new file mode 100644 index 0000000..0a00901 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesEpisode.cs @@ -0,0 +1,24 @@ +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Rating; + +public class RatingMediaSeriesEpisode +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required Guid MediaSeriesEpisodeId { get; set; } + public required long AccountId { get; set; } + public required short Rating { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual MediaSeriesEpisode MediaSeriesEpisode { get; set; } = null!; + public virtual Account.Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs new file mode 100644 index 0000000..f522e07 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingMediaSeriesSeason.cs @@ -0,0 +1,24 @@ +using WatchIt.Database.Model.Media; + +namespace WatchIt.Database.Model.Rating; + +public class RatingMediaSeriesSeason +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required Guid MediaSeriesSeasonId { get; set; } + public required long AccountId { get; set; } + public required short Rating { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual MediaSeriesSeason MediaSeriesSeason { get; set; } = null!; + public virtual Account.Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs new file mode 100644 index 0000000..923c8ba --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonActorRole.cs @@ -0,0 +1,24 @@ +using WatchIt.Database.Model.Person; + +namespace WatchIt.Database.Model.Rating; + +public class RatingPersonActorRole +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required Guid PersonActorRoleId { get; set; } + public required long AccountId { get; set; } + public required short Rating { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual PersonActorRole PersonActorRole { get; set; } = null!; + public virtual Account.Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs new file mode 100644 index 0000000..b0b7ed2 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Rating/RatingPersonCreatorRole.cs @@ -0,0 +1,24 @@ +using WatchIt.Database.Model.Person; + +namespace WatchIt.Database.Model.Rating; + +public class RatingPersonCreatorRole +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required Guid PersonCreatorRoleId { get; set; } + public required long AccountId { get; set; } + public required short Rating { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual PersonCreatorRole PersonCreatorRole { get; set; } = null!; + public virtual Account.Account Account { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs new file mode 100644 index 0000000..2591d73 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountMedia.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.ViewCount; + +public class ViewCountMedia +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long MediaId { get; set; } + public DateOnly Date { get; set; } + public long ViewCount { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Media.Media Media { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs new file mode 100644 index 0000000..61ce17f --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/ViewCount/ViewCountPerson.cs @@ -0,0 +1,21 @@ +namespace WatchIt.Database.Model.ViewCount; + +public class ViewCountPerson +{ + #region PROPERTIES + + public Guid Id { get; set; } + public required long PersonId { get; set; } + public DateOnly Date { get; set; } + public long ViewCount { get; set; } + + #endregion + + + + #region NAVIGATION + + public virtual Person.Person Person { get; set; } = null!; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/WatchIt.Database.Model.csproj b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/WatchIt.Database.Model.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/WatchIt.Database.Model.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/WatchIt.Database/WatchIt.Database/DatabaseContext.cs b/WatchIt.Database/WatchIt.Database/DatabaseContext.cs index da01176..9c3f546 100644 --- a/WatchIt.Database/WatchIt.Database/DatabaseContext.cs +++ b/WatchIt.Database/WatchIt.Database/DatabaseContext.cs @@ -1,126 +1,114 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Principal; +using System.Reflection; +using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; -using WatchIt.Database.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; +using SimpleToolkit.Extensions; using WatchIt.Database.Model.Account; using WatchIt.Database.Model.Common; +using WatchIt.Database.Model.Configuration.Account; using WatchIt.Database.Model.Media; using WatchIt.Database.Model.Person; using WatchIt.Database.Model.Rating; using WatchIt.Database.Model.ViewCount; -namespace WatchIt.Database +namespace WatchIt.Database; + +public class DatabaseContext : DbContext { - public class DatabaseContext : DbContext + #region CONSTRUCTORS + + public DatabaseContext() { - #region CONSTRUCTORS - - public DatabaseContext() { } - - public DatabaseContext(DbContextOptions options) : base(options) { } - - #endregion - - - - #region PROPERTIES - - // Common - public virtual DbSet Countries { get; set; } - public virtual DbSet Genres { get; set; } - public virtual DbSet Genders { get; set; } - - // Account - public virtual DbSet Accounts { get; set; } - public virtual DbSet AccountProfilePictures { get; set; } - public virtual DbSet AccountRefreshTokens { get; set; } - - // Media - public virtual DbSet Media { get; set; } - public virtual DbSet MediaMovies { get; set; } - public virtual DbSet MediaSeries { get; set; } - public virtual DbSet MediaSeriesSeasons { get; set; } - public virtual DbSet MediaSeriesEpisodes { get; set; } - public virtual DbSet MediaPosterImages { get; set; } - public virtual DbSet MediaPhotoImages { get; set; } - public virtual DbSet MediaGenres { get; set; } - public virtual DbSet MediaProductionCountrys { get; set; } - - // Person - public virtual DbSet Persons { get; set; } - public virtual DbSet PersonPhotoImages { get; set; } - public virtual DbSet PersonActorRoles { get; set; } - public virtual DbSet PersonActorRoleTypes { get; set; } - public virtual DbSet PersonCreatorRoles { get; set; } - public virtual DbSet PersonCreatorRoleTypes { get; set; } - - // Rating - public virtual DbSet RatingsMedia { get; set; } - public virtual DbSet RatingsPersonActorRole { get; set; } - public virtual DbSet RatingsPersonCreatorRole { get; set; } - public virtual DbSet RatingsMediaSeriesSeason { get; set; } - public virtual DbSet RatingsMediaSeriesEpisode { get; set; } - - // ViewCount - public virtual DbSet ViewCountsPerson { get; set; } - public virtual DbSet ViewCountsMedia { get; set; } - - - #endregion - - - - #region METHODS - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseNpgsql("name=Default"); - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // Common - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - - // Account - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - - // Media - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - - // Person - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - - // Rating - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - - // ViewCounts - EntityBuilder.Build(modelBuilder); - EntityBuilder.Build(modelBuilder); - } - - #endregion } -} + + public DatabaseContext(DbContextOptions options) : base(options) + { + } + + #endregion + + + + #region PROPERTIES + + // Common + public virtual DbSet Countries { get; set; } + public virtual DbSet Genres { get; set; } + public virtual DbSet Genders { get; set; } + + // Account + public virtual DbSet Accounts { get; set; } + public virtual DbSet AccountProfilePictures { get; set; } + public virtual DbSet AccountRefreshTokens { get; set; } + + // Media + public virtual DbSet Media { get; set; } + public virtual DbSet MediaMovies { get; set; } + public virtual DbSet MediaSeries { get; set; } + public virtual DbSet MediaSeriesSeasons { get; set; } + public virtual DbSet MediaSeriesEpisodes { get; set; } + public virtual DbSet MediaPosterImages { get; set; } + public virtual DbSet MediaPhotoImages { get; set; } + public virtual DbSet MediaGenres { get; set; } + public virtual DbSet MediaProductionCountries { get; set; } + + // Person + public virtual DbSet Persons { get; set; } + public virtual DbSet PersonPhotoImages { get; set; } + public virtual DbSet PersonActorRoles { get; set; } + public virtual DbSet PersonActorRoleTypes { get; set; } + public virtual DbSet PersonCreatorRoles { get; set; } + public virtual DbSet PersonCreatorRoleTypes { get; set; } + + // Rating + public virtual DbSet RatingsMedia { get; set; } + public virtual DbSet RatingsPersonActorRole { get; set; } + public virtual DbSet RatingsPersonCreatorRole { get; set; } + public virtual DbSet RatingsMediaSeriesSeason { get; set; } + public virtual DbSet RatingsMediaSeriesEpisode { get; set; } + + // ViewCount + public virtual DbSet ViewCountsPerson { get; set; } + public virtual DbSet ViewCountsMedia { get; set; } + + #endregion + + + + #region PROTECTED METHODS + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseNpgsql("name=Default"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(AccountConfiguration))); + CreateRootUser(modelBuilder); + } + + protected void CreateRootUser(ModelBuilder modelBuilder) + { + IConfigurationSection configuration = this.GetService().GetSection("RootUser"); + + string leftSalt = StringExtensions.CreateRandom(20); + string rightSalt = StringExtensions.CreateRandom(20); + byte[] hash = SHA512.HashData(Encoding.UTF8.GetBytes($"{leftSalt}{configuration["Password"]}{rightSalt}")); + + modelBuilder.Entity().HasData(new Account + { + Id = 1, + Username = configuration["Username"]!, + Email = configuration["Email"]!, + Password = hash, + LeftSalt = leftSalt, + RightSalt = rightSalt, + IsAdmin = true, + }); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.Designer.cs deleted file mode 100644 index 31e3472..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.Designer.cs +++ /dev/null @@ -1,1198 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240321165113_0000_Initial")] - partial class _0000_Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .HasColumnType("boolean"); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Gender"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.Designer.cs deleted file mode 100644 index 9b28541..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.Designer.cs +++ /dev/null @@ -1,1213 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240321165250_0001_GendersTableAdded")] - partial class _0001_GendersTableAdded - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .HasColumnType("boolean"); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genders"); - - b.HasData( - new - { - Id = (short)1, - Name = "Male" - }, - new - { - Id = (short)2, - Name = "Female" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.cs deleted file mode 100644 index a45939d..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321165250_0001_GendersTableAdded.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0001_GendersTableAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Accounts_Gender_GenderId", - table: "Accounts"); - - migrationBuilder.DropForeignKey( - name: "FK_Persons_Gender_GenderId", - table: "Persons"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Gender", - table: "Gender"); - - migrationBuilder.RenameTable( - name: "Gender", - newName: "Genders"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Genders", - table: "Genders", - column: "Id"); - - migrationBuilder.InsertData( - table: "Genders", - columns: new[] { "Id", "Name" }, - values: new object[,] - { - { (short)1, "Male" }, - { (short)2, "Female" } - }); - - migrationBuilder.CreateIndex( - name: "IX_Genders_Id", - table: "Genders", - column: "Id", - unique: true); - - migrationBuilder.AddForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts", - column: "GenderId", - principalTable: "Genders", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - - migrationBuilder.AddForeignKey( - name: "FK_Persons_Genders_GenderId", - table: "Persons", - column: "GenderId", - principalTable: "Genders", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts"); - - migrationBuilder.DropForeignKey( - name: "FK_Persons_Genders_GenderId", - table: "Persons"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Genders", - table: "Genders"); - - migrationBuilder.DropIndex( - name: "IX_Genders_Id", - table: "Genders"); - - migrationBuilder.DeleteData( - table: "Genders", - keyColumn: "Id", - keyValue: (short)1); - - migrationBuilder.DeleteData( - table: "Genders", - keyColumn: "Id", - keyValue: (short)2); - - migrationBuilder.RenameTable( - name: "Genders", - newName: "Gender"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Gender", - table: "Gender", - column: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_Accounts_Gender_GenderId", - table: "Accounts", - column: "GenderId", - principalTable: "Gender", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - - migrationBuilder.AddForeignKey( - name: "FK_Persons_Gender_GenderId", - table: "Persons", - column: "GenderId", - principalTable: "Gender", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.Designer.cs deleted file mode 100644 index 51cecb8..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.Designer.cs +++ /dev/null @@ -1,1297 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240321171215_0002_ViewCountTablesAdded")] - partial class _0002_ViewCountTablesAdded - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .HasColumnType("boolean"); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genders"); - - b.HasData( - new - { - Id = (short)1, - Name = "Male" - }, - new - { - Id = (short)2, - Name = "Female" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("ViewCountsMedia", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonId"); - - b.ToTable("ViewCountsPerson", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("ViewCountsMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("ViewCountsPerson") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Person"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - - b.Navigation("ViewCountsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("ViewCountsPerson"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.cs deleted file mode 100644 index e1a940c..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321171215_0002_ViewCountTablesAdded.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0002_ViewCountTablesAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ViewCountsMedia", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - MediaId = table.Column(type: "bigint", nullable: false), - Date = table.Column(type: "date", nullable: false, defaultValueSql: "now()"), - ViewCount = table.Column(type: "bigint", nullable: false, defaultValue: 0L) - }, - constraints: table => - { - table.PrimaryKey("PK_ViewCountsMedia", x => x.Id); - table.ForeignKey( - name: "FK_ViewCountsMedia_Media_MediaId", - column: x => x.MediaId, - principalTable: "Media", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ViewCountsPerson", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - PersonId = table.Column(type: "bigint", nullable: false), - Date = table.Column(type: "date", nullable: false, defaultValueSql: "now()"), - ViewCount = table.Column(type: "bigint", nullable: false, defaultValue: 0L) - }, - constraints: table => - { - table.PrimaryKey("PK_ViewCountsPerson", x => x.Id); - table.ForeignKey( - name: "FK_ViewCountsPerson_Persons_PersonId", - column: x => x.PersonId, - principalTable: "Persons", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_ViewCountsMedia_Id", - table: "ViewCountsMedia", - column: "Id", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ViewCountsMedia_MediaId", - table: "ViewCountsMedia", - column: "MediaId"); - - migrationBuilder.CreateIndex( - name: "IX_ViewCountsPerson_Id", - table: "ViewCountsPerson", - column: "Id", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ViewCountsPerson_PersonId", - table: "ViewCountsPerson", - column: "PersonId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ViewCountsMedia"); - - migrationBuilder.DropTable( - name: "ViewCountsPerson"); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs deleted file mode 100644 index e4bfd8b..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.Designer.cs +++ /dev/null @@ -1,1297 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin")] - partial class _0003_GenderNotRequiredAndDefaultNotAdmin - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genders"); - - b.HasData( - new - { - Id = (short)1, - Name = "Male" - }, - new - { - Id = (short)2, - Name = "Female" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("ViewCountsMedia", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonId"); - - b.ToTable("ViewCountsPerson", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId"); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("ViewCountsMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("ViewCountsPerson") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Person"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - - b.Navigation("ViewCountsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("ViewCountsPerson"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs deleted file mode 100644 index 9c843f9..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324143239_0003_GenderNotRequiredAndDefaultNotAdmin.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0003_GenderNotRequiredAndDefaultNotAdmin : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts"); - - migrationBuilder.AlterColumn( - name: "IsAdmin", - table: "Accounts", - type: "boolean", - nullable: false, - defaultValue: false, - oldClrType: typeof(bool), - oldType: "boolean"); - - migrationBuilder.AlterColumn( - name: "GenderId", - table: "Accounts", - type: "smallint", - nullable: true, - oldClrType: typeof(short), - oldType: "smallint"); - - migrationBuilder.AddForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts", - column: "GenderId", - principalTable: "Genders", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts"); - - migrationBuilder.AlterColumn( - name: "IsAdmin", - table: "Accounts", - type: "boolean", - nullable: false, - oldClrType: typeof(bool), - oldType: "boolean", - oldDefaultValue: false); - - migrationBuilder.AlterColumn( - name: "GenderId", - table: "Accounts", - type: "smallint", - nullable: false, - defaultValue: (short)0, - oldClrType: typeof(short), - oldType: "smallint", - oldNullable: true); - - migrationBuilder.AddForeignKey( - name: "FK_Accounts_Genders_GenderId", - table: "Accounts", - column: "GenderId", - principalTable: "Genders", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs deleted file mode 100644 index 77f4446..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.Designer.cs +++ /dev/null @@ -1,1296 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240324144605_0004_AccountDescriptionNullable")] - partial class _0004_AccountDescriptionNullable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genders"); - - b.HasData( - new - { - Id = (short)1, - Name = "Male" - }, - new - { - Id = (short)2, - Name = "Female" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("ViewCountsMedia", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonId"); - - b.ToTable("ViewCountsPerson", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId"); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("ViewCountsMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("ViewCountsPerson") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Person"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - - b.Navigation("ViewCountsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("ViewCountsPerson"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs deleted file mode 100644 index db5b153..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324144605_0004_AccountDescriptionNullable.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0004_AccountDescriptionNullable : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Description", - table: "Accounts", - type: "character varying(1000)", - maxLength: 1000, - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(1000)", - oldMaxLength: 1000); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Description", - table: "Accounts", - type: "character varying(1000)", - maxLength: 1000, - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "character varying(1000)", - oldMaxLength: 1000, - oldNullable: true); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs deleted file mode 100644 index eadf7d4..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.Designer.cs +++ /dev/null @@ -1,1331 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using WatchIt.Database; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20240324195235_0005_AccountRefreshTokensAdded")] - partial class _0005_AccountRefreshTokensAdded - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BackgroundPictureId") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(320) - .HasColumnType("character varying(320)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("IsAdmin") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("LastActive") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("LeftSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Password") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("bytea"); - - b.Property("ProfilePictureId") - .HasColumnType("uuid"); - - b.Property("RightSalt") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Username") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Id"); - - b.HasIndex("BackgroundPictureId"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("ProfilePictureId") - .IsUnique(); - - b.ToTable("Accounts"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountProfilePictures"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("Lifetime") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("AccountRefreshTokens"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Countries"); - - b.HasData( - new - { - Id = (short)1, - Name = "Afghanistan" - }, - new - { - Id = (short)2, - Name = "Albania" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Gender", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genders"); - - b.HasData( - new - { - Id = (short)1, - Name = "Male" - }, - new - { - Id = (short)2, - Name = "Female" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("Genres"); - - b.HasData( - new - { - Id = (short)1, - Name = "Comedy" - }, - new - { - Id = (short)2, - Name = "Thriller" - }, - new - { - Id = (short)3, - Name = "Horror" - }, - new - { - Id = (short)4, - Name = "Action" - }, - new - { - Id = (short)5, - Name = "Drama" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Property("Id") - .HasColumnType("bigint"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Length") - .HasColumnType("interval"); - - b.Property("MediaPosterImageId") - .HasColumnType("uuid"); - - b.Property("OriginalTitle") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaPosterImageId") - .IsUnique(); - - b.ToTable("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.Property("GenreId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("GenreId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Budget") - .HasColumnType("money"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaMovies"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("IsMediaBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("IsUniversalBackground") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("MediaPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaPosterImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.Property("CountryId") - .HasColumnType("smallint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.HasKey("CountryId", "MediaId"); - - b.HasIndex("MediaId"); - - b.ToTable("MediaProductionCountrys"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HasEnded") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IsSpecial") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("MediaSeriesEpisodes"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaSeriesId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Number") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesId"); - - b.ToTable("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("DeathDate") - .HasColumnType("date"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("FullName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("GenderId") - .HasColumnType("smallint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PersonPhotoId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("GenderId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonPhotoId") - .IsUnique(); - - b.ToTable("Persons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("RoleName") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonActorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonActorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonActorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Actor" - }, - new - { - Id = (short)2, - Name = "Supporting actor" - }, - new - { - Id = (short)3, - Name = "Voice actor" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleTypeId") - .HasColumnType("smallint"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.HasIndex("PersonCreatorRoleTypeId"); - - b.HasIndex("PersonId"); - - b.ToTable("PersonCreatorRoles"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRoleType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("smallint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonCreatorRoleTypes"); - - b.HasData( - new - { - Id = (short)1, - Name = "Director" - }, - new - { - Id = (short)2, - Name = "Producer" - }, - new - { - Id = (short)3, - Name = "Screenwriter" - }); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Image") - .IsRequired() - .HasMaxLength(-1) - .HasColumnType("bytea"); - - b.Property("MimeType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UploadDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.ToTable("PersonPhotoImages"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("RatingsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesEpisodeId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesEpisodeId"); - - b.ToTable("RatingsMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("MediaSeriesSeasonId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaSeriesSeasonId"); - - b.ToTable("RatingsMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonActorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonActorRoleId"); - - b.ToTable("RatingsPersonActorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccountId") - .HasColumnType("bigint"); - - b.Property("PersonCreatorRoleId") - .HasColumnType("uuid"); - - b.Property("Rating") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("AccountId"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonCreatorRoleId"); - - b.ToTable("RatingsPersonCreatorRole", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("MediaId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("MediaId"); - - b.ToTable("ViewCountsMedia", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Date") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasDefaultValueSql("now()"); - - b.Property("PersonId") - .HasColumnType("bigint"); - - b.Property("ViewCount") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("Id") - .IsUnique(); - - b.HasIndex("PersonId"); - - b.ToTable("ViewCountsPerson", (string)null); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaPhotoImage", "BackgroundPicture") - .WithMany() - .HasForeignKey("BackgroundPictureId"); - - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId"); - - b.HasOne("WatchIt.Database.Model.Account.AccountProfilePicture", "ProfilePicture") - .WithOne("Account") - .HasForeignKey("WatchIt.Database.Model.Account.Account", "ProfilePictureId"); - - b.Navigation("BackgroundPicture"); - - b.Navigation("Gender"); - - b.Navigation("ProfilePicture"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountRefreshToken", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("AccountRefreshTokens") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); - - b.Navigation("MediaPosterImage"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaGenre", b => - { - b.HasOne("WatchIt.Database.Model.Common.Genre", "Genre") - .WithMany("MediaGenres") - .HasForeignKey("GenreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaGenres") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Genre"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaPhotoImages") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaProductionCountry", b => - { - b.HasOne("WatchIt.Database.Model.Common.Country", "Country") - .WithMany("MediaProductionCountries") - .HasForeignKey("CountryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("MediaProductionCountries") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Country"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("MediaSeriesEpisodes") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", "MediaSeries") - .WithMany("MediaSeriesSeasons") - .HasForeignKey("MediaSeriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MediaSeries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") - .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") - .WithOne("Person") - .HasForeignKey("WatchIt.Database.Model.Person.Person", "PersonPhotoId"); - - b.Navigation("Gender"); - - b.Navigation("PersonPhoto"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonActorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRoleType", "PersonActorRoleType") - .WithMany() - .HasForeignKey("PersonActorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonActorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonActorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("PersonCreatorRoles") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRoleType", "PersonCreatorRoleType") - .WithMany() - .HasForeignKey("PersonCreatorRoleTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("PersonCreatorRoles") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - - b.Navigation("Person"); - - b.Navigation("PersonCreatorRoleType"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMedia", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMedia") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("RatingMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesEpisode", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesEpisode", "MediaSeriesEpisode") - .WithMany("RatingMediaSeriesEpisode") - .HasForeignKey("MediaSeriesEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingMediaSeriesSeason", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") - .WithMany("RatingMediaSeriesSeason") - .HasForeignKey("MediaSeriesSeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("MediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonActorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonActorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonActorRole", "PersonActorRole") - .WithMany("RatingPersonActorRole") - .HasForeignKey("PersonActorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Rating.RatingPersonCreatorRole", b => - { - b.HasOne("WatchIt.Database.Model.Account.Account", "Account") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Person.PersonCreatorRole", "PersonCreatorRole") - .WithMany("RatingPersonCreatorRole") - .HasForeignKey("PersonCreatorRoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Account"); - - b.Navigation("PersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountMedia", b => - { - b.HasOne("WatchIt.Database.Model.Media.Media", "Media") - .WithMany("ViewCountsMedia") - .HasForeignKey("MediaId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Media"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.ViewCount.ViewCountPerson", b => - { - b.HasOne("WatchIt.Database.Model.Person.Person", "Person") - .WithMany("ViewCountsPerson") - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Person"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.Account", b => - { - b.Navigation("AccountRefreshTokens"); - - b.Navigation("RatingMedia"); - - b.Navigation("RatingMediaSeriesEpisode"); - - b.Navigation("RatingMediaSeriesSeason"); - - b.Navigation("RatingPersonActorRole"); - - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => - { - b.Navigation("Account") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Country", b => - { - b.Navigation("MediaProductionCountries"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Common.Genre", b => - { - b.Navigation("MediaGenres"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => - { - b.Navigation("MediaGenres"); - - b.Navigation("MediaPhotoImages"); - - b.Navigation("MediaProductionCountries"); - - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("RatingMedia"); - - b.Navigation("ViewCountsMedia"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => - { - b.Navigation("Media") - .IsRequired(); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => - { - b.Navigation("Media") - .IsRequired(); - - b.Navigation("MediaSeriesSeasons"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => - { - b.Navigation("RatingMediaSeriesEpisode"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesSeason", b => - { - b.Navigation("MediaSeriesEpisodes"); - - b.Navigation("RatingMediaSeriesSeason"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.Person", b => - { - b.Navigation("PersonActorRoles"); - - b.Navigation("PersonCreatorRoles"); - - b.Navigation("ViewCountsPerson"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonActorRole", b => - { - b.Navigation("RatingPersonActorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonCreatorRole", b => - { - b.Navigation("RatingPersonCreatorRole"); - }); - - modelBuilder.Entity("WatchIt.Database.Model.Person.PersonPhotoImage", b => - { - b.Navigation("Person") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs deleted file mode 100644 index 7e5f5cf..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324195235_0005_AccountRefreshTokensAdded.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0005_AccountRefreshTokensAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AccountRefreshTokens", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - AccountId = table.Column(type: "bigint", nullable: false), - Lifetime = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AccountRefreshTokens", x => x.Id); - table.ForeignKey( - name: "FK_AccountRefreshTokens_Accounts_AccountId", - column: x => x.AccountId, - principalTable: "Accounts", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AccountRefreshTokens_AccountId", - table: "AccountRefreshTokens", - column: "AccountId"); - - migrationBuilder.CreateIndex( - name: "IX_AccountRefreshTokens_Id", - table: "AccountRefreshTokens", - column: "Id", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AccountRefreshTokens"); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs deleted file mode 100644 index c02ef15..0000000 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace WatchIt.Database.Migrations -{ - /// - public partial class _0006_AccountRefreshTokenChanges : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Lifetime", - table: "AccountRefreshTokens", - newName: "ExpirationDate"); - - migrationBuilder.AddColumn( - name: "IsExtendable", - table: "AccountRefreshTokens", - type: "boolean", - nullable: false, - defaultValue: false); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IsExtendable", - table: "AccountRefreshTokens"); - - migrationBuilder.RenameColumn( - name: "ExpirationDate", - table: "AccountRefreshTokens", - newName: "Lifetime"); - } - } -} diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.Designer.cs similarity index 95% rename from WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs rename to WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.Designer.cs index df17bdb..c7d5ee2 100644 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240324205952_0006_AccountRefreshTokenChanges.Designer.cs +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.Designer.cs @@ -12,15 +12,18 @@ using WatchIt.Database; namespace WatchIt.Database.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20240324205952_0006_AccountRefreshTokenChanges")] - partial class _0006_AccountRefreshTokenChanges + [Migration("20240418190404_0001_Initial")] + partial class _0001_Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -99,6 +102,20 @@ namespace WatchIt.Database.Migrations .IsUnique(); b.ToTable("Accounts"); + + b.HasData( + new + { + Id = 1L, + CreationDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "root@watch.it", + IsAdmin = true, + LastActive = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + LeftSalt = "qE]Q^g%tU\"6Uu^GfE:V:", + Password = new byte[] { 165, 250, 135, 31, 187, 161, 15, 246, 18, 232, 64, 25, 37, 173, 91, 111, 140, 177, 183, 84, 254, 177, 15, 235, 119, 219, 29, 169, 32, 108, 187, 121, 204, 51, 213, 28, 141, 89, 91, 226, 0, 23, 7, 91, 139, 230, 151, 104, 62, 91, 59, 6, 207, 26, 200, 141, 104, 5, 151, 201, 243, 163, 28, 248 }, + RightSalt = "T7j)~.#%~ZtOFUZFK,K+", + Username = "root" + }); }); modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => @@ -163,6 +180,11 @@ namespace WatchIt.Database.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("IsHistorical") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + b.Property("Name") .IsRequired() .HasMaxLength(100) @@ -179,11 +201,13 @@ namespace WatchIt.Database.Migrations new { Id = (short)1, + IsHistorical = false, Name = "Afghanistan" }, new { Id = (short)2, + IsHistorical = false, Name = "Albania" }); }); @@ -229,8 +253,7 @@ namespace WatchIt.Database.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); + .HasColumnType("text"); b.Property("Name") .IsRequired() @@ -275,14 +298,17 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("bigint"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Description") .HasMaxLength(1000) .HasColumnType("character varying(1000)"); - b.Property("Length") - .HasColumnType("interval"); + b.Property("Length") + .HasColumnType("smallint"); b.Property("MediaPosterImageId") .HasColumnType("uuid"); @@ -291,8 +317,8 @@ namespace WatchIt.Database.Migrations .HasMaxLength(250) .HasColumnType("character varying(250)"); - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); + b.Property("ReleaseDate") + .HasColumnType("date"); b.Property("Title") .IsRequired() @@ -328,11 +354,8 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("bigint"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("Budget") .HasColumnType("money"); @@ -435,13 +458,12 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("bigint"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("HasEnded") - .HasColumnType("boolean"); + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); b.HasKey("Id"); @@ -528,7 +550,7 @@ namespace WatchIt.Database.Migrations .HasMaxLength(200) .HasColumnType("character varying(200)"); - b.Property("GenderId") + b.Property("GenderId") .HasColumnType("smallint"); b.Property("Name") @@ -946,18 +968,6 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") .WithOne("Media") .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); @@ -984,6 +994,17 @@ namespace WatchIt.Database.Migrations b.Navigation("Media"); }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithOne() + .HasForeignKey("WatchIt.Database.Model.Media.MediaMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => { b.HasOne("WatchIt.Database.Model.Media.Media", "Media") @@ -1014,6 +1035,17 @@ namespace WatchIt.Database.Migrations b.Navigation("Media"); }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithOne() + .HasForeignKey("WatchIt.Database.Model.Media.MediaSeries", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => { b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") @@ -1040,9 +1072,7 @@ namespace WatchIt.Database.Migrations { b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("GenderId"); b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") .WithOne("Person") @@ -1272,12 +1302,6 @@ namespace WatchIt.Database.Migrations b.Navigation("ViewCountsMedia"); }); - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => { b.Navigation("Media") @@ -1286,9 +1310,6 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => { - b.Navigation("Media") - .IsRequired(); - b.Navigation("MediaSeriesSeasons"); }); diff --git a/WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.cs b/WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.cs similarity index 86% rename from WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.cs rename to WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.cs index 775d717..4663b36 100644 --- a/WatchIt.Database/WatchIt.Database/Migrations/20240321165113_0000_Initial.cs +++ b/WatchIt.Database/WatchIt.Database/Migrations/20240418190404_0001_Initial.cs @@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace WatchIt.Database.Migrations { /// - public partial class _0000_Initial : Migration + public partial class _0001_Initial : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -34,7 +34,8 @@ namespace WatchIt.Database.Migrations { Id = table.Column(type: "smallint", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false) + Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + IsHistorical = table.Column(type: "boolean", nullable: false, defaultValue: false) }, constraints: table => { @@ -42,7 +43,7 @@ namespace WatchIt.Database.Migrations }); migrationBuilder.CreateTable( - name: "Gender", + name: "Genders", columns: table => new { Id = table.Column(type: "smallint", nullable: false) @@ -51,7 +52,7 @@ namespace WatchIt.Database.Migrations }, constraints: table => { - table.PrimaryKey("PK_Gender", x => x.Id); + table.PrimaryKey("PK_Genders", x => x.Id); }); migrationBuilder.CreateTable( @@ -61,26 +62,13 @@ namespace WatchIt.Database.Migrations Id = table.Column(type: "smallint", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true) + Description = table.Column(type: "text", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Genres", x => x.Id); }); - migrationBuilder.CreateTable( - name: "MediaMovies", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Budget = table.Column(type: "money", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MediaMovies", x => x.Id); - }); - migrationBuilder.CreateTable( name: "MediaPosterImages", columns: table => new @@ -95,19 +83,6 @@ namespace WatchIt.Database.Migrations table.PrimaryKey("PK_MediaPosterImages", x => x.Id); }); - migrationBuilder.CreateTable( - name: "MediaSeries", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - HasEnded = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_MediaSeries", x => x.Id); - }); - migrationBuilder.CreateTable( name: "PersonActorRoleTypes", columns: table => new @@ -152,54 +127,23 @@ namespace WatchIt.Database.Migrations name: "Media", columns: table => new { - Id = table.Column(type: "bigint", nullable: false), + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Title = table.Column(type: "character varying(250)", maxLength: 250, nullable: false), OriginalTitle = table.Column(type: "character varying(250)", maxLength: 250, nullable: true), Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - ReleaseDate = table.Column(type: "timestamp with time zone", nullable: true), - Length = table.Column(type: "interval", nullable: true), + ReleaseDate = table.Column(type: "date", nullable: true), + Length = table.Column(type: "smallint", nullable: true), MediaPosterImageId = table.Column(type: "uuid", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Media", x => x.Id); - table.ForeignKey( - name: "FK_Media_MediaMovies_Id", - column: x => x.Id, - principalTable: "MediaMovies", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_Media_MediaPosterImages_MediaPosterImageId", column: x => x.MediaPosterImageId, principalTable: "MediaPosterImages", principalColumn: "Id"); - table.ForeignKey( - name: "FK_Media_MediaSeries_Id", - column: x => x.Id, - principalTable: "MediaSeries", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "MediaSeriesSeasons", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - MediaSeriesId = table.Column(type: "bigint", nullable: false), - Number = table.Column(type: "smallint", nullable: false), - Name = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MediaSeriesSeasons", x => x.Id); - table.ForeignKey( - name: "FK_MediaSeriesSeasons_MediaSeries_MediaSeriesId", - column: x => x.MediaSeriesId, - principalTable: "MediaSeries", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( @@ -213,18 +157,17 @@ namespace WatchIt.Database.Migrations Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), BirthDate = table.Column(type: "date", nullable: true), DeathDate = table.Column(type: "date", nullable: true), - GenderId = table.Column(type: "smallint", nullable: false), + GenderId = table.Column(type: "smallint", nullable: true), PersonPhotoId = table.Column(type: "uuid", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Persons", x => x.Id); table.ForeignKey( - name: "FK_Persons_Gender_GenderId", + name: "FK_Persons_Genders_GenderId", column: x => x.GenderId, - principalTable: "Gender", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalTable: "Genders", + principalColumn: "Id"); table.ForeignKey( name: "FK_Persons_PersonPhotoImages_PersonPhotoId", column: x => x.PersonPhotoId, @@ -256,6 +199,24 @@ namespace WatchIt.Database.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "MediaMovies", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false), + Budget = table.Column(type: "money", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaMovies", x => x.Id); + table.ForeignKey( + name: "FK_MediaMovies_Media_Id", + column: x => x.Id, + principalTable: "Media", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "MediaPhotoImages", columns: table => new @@ -304,22 +265,39 @@ namespace WatchIt.Database.Migrations }); migrationBuilder.CreateTable( - name: "MediaSeriesEpisodes", + name: "MediaSeries", columns: table => new { - Id = table.Column(type: "uuid", nullable: false), - MediaSeriesSeasonId = table.Column(type: "uuid", nullable: false), - Number = table.Column(type: "smallint", nullable: false), - Name = table.Column(type: "text", nullable: true), - IsSpecial = table.Column(type: "boolean", nullable: false, defaultValue: false) + Id = table.Column(type: "bigint", nullable: false), + HasEnded = table.Column(type: "boolean", nullable: false, defaultValue: false) }, constraints: table => { - table.PrimaryKey("PK_MediaSeriesEpisodes", x => x.Id); + table.PrimaryKey("PK_MediaSeries", x => x.Id); table.ForeignKey( - name: "FK_MediaSeriesEpisodes_MediaSeriesSeasons_MediaSeriesSeasonId", - column: x => x.MediaSeriesSeasonId, - principalTable: "MediaSeriesSeasons", + name: "FK_MediaSeries_Media_Id", + column: x => x.Id, + principalTable: "Media", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ViewCountsMedia", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MediaId = table.Column(type: "bigint", nullable: false), + Date = table.Column(type: "date", nullable: false, defaultValueSql: "now()"), + ViewCount = table.Column(type: "bigint", nullable: false, defaultValue: 0L) + }, + constraints: table => + { + table.PrimaryKey("PK_ViewCountsMedia", x => x.Id); + table.ForeignKey( + name: "FK_ViewCountsMedia_Media_MediaId", + column: x => x.MediaId, + principalTable: "Media", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -389,6 +367,26 @@ namespace WatchIt.Database.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "ViewCountsPerson", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PersonId = table.Column(type: "bigint", nullable: false), + Date = table.Column(type: "date", nullable: false, defaultValueSql: "now()"), + ViewCount = table.Column(type: "bigint", nullable: false, defaultValue: 0L) + }, + constraints: table => + { + table.PrimaryKey("PK_ViewCountsPerson", x => x.Id); + table.ForeignKey( + name: "FK_ViewCountsPerson_Persons_PersonId", + column: x => x.PersonId, + principalTable: "Persons", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "Accounts", columns: table => new @@ -397,14 +395,14 @@ namespace WatchIt.Database.Migrations .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Username = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), Email = table.Column(type: "character varying(320)", maxLength: 320, nullable: false), - Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: false), - GenderId = table.Column(type: "smallint", nullable: false), + Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + GenderId = table.Column(type: "smallint", nullable: true), ProfilePictureId = table.Column(type: "uuid", nullable: true), BackgroundPictureId = table.Column(type: "uuid", nullable: true), Password = table.Column(type: "bytea", maxLength: 1000, nullable: false), LeftSalt = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), RightSalt = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - IsAdmin = table.Column(type: "boolean", nullable: false), + IsAdmin = table.Column(type: "boolean", nullable: false, defaultValue: false), CreationDate = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), LastActive = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") }, @@ -417,11 +415,10 @@ namespace WatchIt.Database.Migrations principalTable: "AccountProfilePictures", principalColumn: "Id"); table.ForeignKey( - name: "FK_Accounts_Gender_GenderId", + name: "FK_Accounts_Genders_GenderId", column: x => x.GenderId, - principalTable: "Gender", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalTable: "Genders", + principalColumn: "Id"); table.ForeignKey( name: "FK_Accounts_MediaPhotoImages_BackgroundPictureId", column: x => x.BackgroundPictureId, @@ -429,6 +426,46 @@ namespace WatchIt.Database.Migrations principalColumn: "Id"); }); + migrationBuilder.CreateTable( + name: "MediaSeriesSeasons", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MediaSeriesId = table.Column(type: "bigint", nullable: false), + Number = table.Column(type: "smallint", nullable: false), + Name = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaSeriesSeasons", x => x.Id); + table.ForeignKey( + name: "FK_MediaSeriesSeasons_MediaSeries_MediaSeriesId", + column: x => x.MediaSeriesId, + principalTable: "MediaSeries", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AccountRefreshTokens", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + AccountId = table.Column(type: "bigint", nullable: false), + ExpirationDate = table.Column(type: "timestamp with time zone", nullable: false), + IsExtendable = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountRefreshTokens", x => x.Id); + table.ForeignKey( + name: "FK_AccountRefreshTokens_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "RatingsMedia", columns: table => new @@ -455,58 +492,6 @@ namespace WatchIt.Database.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "RatingsMediaSeriesEpisode", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - MediaSeriesEpisodeId = table.Column(type: "uuid", nullable: false), - AccountId = table.Column(type: "bigint", nullable: false), - Rating = table.Column(type: "smallint", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_RatingsMediaSeriesEpisode", x => x.Id); - table.ForeignKey( - name: "FK_RatingsMediaSeriesEpisode_Accounts_AccountId", - column: x => x.AccountId, - principalTable: "Accounts", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_RatingsMediaSeriesEpisode_MediaSeriesEpisodes_MediaSeriesEp~", - column: x => x.MediaSeriesEpisodeId, - principalTable: "MediaSeriesEpisodes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "RatingsMediaSeriesSeason", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - MediaSeriesSeasonId = table.Column(type: "uuid", nullable: false), - AccountId = table.Column(type: "bigint", nullable: false), - Rating = table.Column(type: "smallint", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_RatingsMediaSeriesSeason", x => x.Id); - table.ForeignKey( - name: "FK_RatingsMediaSeriesSeason_Accounts_AccountId", - column: x => x.AccountId, - principalTable: "Accounts", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_RatingsMediaSeriesSeason_MediaSeriesSeasons_MediaSeriesSeas~", - column: x => x.MediaSeriesSeasonId, - principalTable: "MediaSeriesSeasons", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateTable( name: "RatingsPersonActorRole", columns: table => new @@ -559,6 +544,84 @@ namespace WatchIt.Database.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "MediaSeriesEpisodes", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MediaSeriesSeasonId = table.Column(type: "uuid", nullable: false), + Number = table.Column(type: "smallint", nullable: false), + Name = table.Column(type: "text", nullable: true), + IsSpecial = table.Column(type: "boolean", nullable: false, defaultValue: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaSeriesEpisodes", x => x.Id); + table.ForeignKey( + name: "FK_MediaSeriesEpisodes_MediaSeriesSeasons_MediaSeriesSeasonId", + column: x => x.MediaSeriesSeasonId, + principalTable: "MediaSeriesSeasons", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "RatingsMediaSeriesSeason", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MediaSeriesSeasonId = table.Column(type: "uuid", nullable: false), + AccountId = table.Column(type: "bigint", nullable: false), + Rating = table.Column(type: "smallint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RatingsMediaSeriesSeason", x => x.Id); + table.ForeignKey( + name: "FK_RatingsMediaSeriesSeason_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_RatingsMediaSeriesSeason_MediaSeriesSeasons_MediaSeriesSeas~", + column: x => x.MediaSeriesSeasonId, + principalTable: "MediaSeriesSeasons", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "RatingsMediaSeriesEpisode", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MediaSeriesEpisodeId = table.Column(type: "uuid", nullable: false), + AccountId = table.Column(type: "bigint", nullable: false), + Rating = table.Column(type: "smallint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RatingsMediaSeriesEpisode", x => x.Id); + table.ForeignKey( + name: "FK_RatingsMediaSeriesEpisode_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_RatingsMediaSeriesEpisode_MediaSeriesEpisodes_MediaSeriesEp~", + column: x => x.MediaSeriesEpisodeId, + principalTable: "MediaSeriesEpisodes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Accounts", + columns: new[] { "Id", "BackgroundPictureId", "Description", "Email", "GenderId", "IsAdmin", "LeftSalt", "Password", "ProfilePictureId", "RightSalt", "Username" }, + values: new object[] { 1L, null, null, "root@watch.it", null, true, "qE]Q^g%tU\"6Uu^GfE:V:", new byte[] { 165, 250, 135, 31, 187, 161, 15, 246, 18, 232, 64, 25, 37, 173, 91, 111, 140, 177, 183, 84, 254, 177, 15, 235, 119, 219, 29, 169, 32, 108, 187, 121, 204, 51, 213, 28, 141, 89, 91, 226, 0, 23, 7, 91, 139, 230, 151, 104, 62, 91, 59, 6, 207, 26, 200, 141, 104, 5, 151, 201, 243, 163, 28, 248 }, null, "T7j)~.#%~ZtOFUZFK,K+", "root" }); + migrationBuilder.InsertData( table: "Countries", columns: new[] { "Id", "Name" }, @@ -568,6 +631,15 @@ namespace WatchIt.Database.Migrations { (short)2, "Albania" } }); + migrationBuilder.InsertData( + table: "Genders", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { (short)1, "Male" }, + { (short)2, "Female" } + }); + migrationBuilder.InsertData( table: "Genres", columns: new[] { "Id", "Description", "Name" }, @@ -606,6 +678,17 @@ namespace WatchIt.Database.Migrations column: "Id", unique: true); + migrationBuilder.CreateIndex( + name: "IX_AccountRefreshTokens_AccountId", + table: "AccountRefreshTokens", + column: "AccountId"); + + migrationBuilder.CreateIndex( + name: "IX_AccountRefreshTokens_Id", + table: "AccountRefreshTokens", + column: "Id", + unique: true); + migrationBuilder.CreateIndex( name: "IX_Accounts_BackgroundPictureId", table: "Accounts", @@ -634,6 +717,12 @@ namespace WatchIt.Database.Migrations column: "Id", unique: true); + migrationBuilder.CreateIndex( + name: "IX_Genders_Id", + table: "Genders", + column: "Id", + unique: true); + migrationBuilder.CreateIndex( name: "IX_Genres_Id", table: "Genres", @@ -869,14 +958,42 @@ namespace WatchIt.Database.Migrations name: "IX_RatingsPersonCreatorRole_PersonCreatorRoleId", table: "RatingsPersonCreatorRole", column: "PersonCreatorRoleId"); + + migrationBuilder.CreateIndex( + name: "IX_ViewCountsMedia_Id", + table: "ViewCountsMedia", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ViewCountsMedia_MediaId", + table: "ViewCountsMedia", + column: "MediaId"); + + migrationBuilder.CreateIndex( + name: "IX_ViewCountsPerson_Id", + table: "ViewCountsPerson", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ViewCountsPerson_PersonId", + table: "ViewCountsPerson", + column: "PersonId"); } /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AccountRefreshTokens"); + migrationBuilder.DropTable( name: "MediaGenres"); + migrationBuilder.DropTable( + name: "MediaMovies"); + migrationBuilder.DropTable( name: "MediaProductionCountrys"); @@ -895,6 +1012,12 @@ namespace WatchIt.Database.Migrations migrationBuilder.DropTable( name: "RatingsPersonCreatorRole"); + migrationBuilder.DropTable( + name: "ViewCountsMedia"); + + migrationBuilder.DropTable( + name: "ViewCountsPerson"); + migrationBuilder.DropTable( name: "Genres"); @@ -932,22 +1055,19 @@ namespace WatchIt.Database.Migrations name: "Persons"); migrationBuilder.DropTable( - name: "Media"); + name: "MediaSeries"); migrationBuilder.DropTable( - name: "Gender"); + name: "Genders"); migrationBuilder.DropTable( name: "PersonPhotoImages"); migrationBuilder.DropTable( - name: "MediaMovies"); + name: "Media"); migrationBuilder.DropTable( name: "MediaPosterImages"); - - migrationBuilder.DropTable( - name: "MediaSeries"); } } } diff --git a/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs b/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs index 186e9cc..2318c69 100644 --- a/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs +++ b/WatchIt.Database/WatchIt.Database/Migrations/DatabaseContextModelSnapshot.cs @@ -17,7 +17,10 @@ namespace WatchIt.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -96,6 +99,20 @@ namespace WatchIt.Database.Migrations .IsUnique(); b.ToTable("Accounts"); + + b.HasData( + new + { + Id = 1L, + CreationDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "root@watch.it", + IsAdmin = true, + LastActive = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + LeftSalt = "qE]Q^g%tU\"6Uu^GfE:V:", + Password = new byte[] { 165, 250, 135, 31, 187, 161, 15, 246, 18, 232, 64, 25, 37, 173, 91, 111, 140, 177, 183, 84, 254, 177, 15, 235, 119, 219, 29, 169, 32, 108, 187, 121, 204, 51, 213, 28, 141, 89, 91, 226, 0, 23, 7, 91, 139, 230, 151, 104, 62, 91, 59, 6, 207, 26, 200, 141, 104, 5, 151, 201, 243, 163, 28, 248 }, + RightSalt = "T7j)~.#%~ZtOFUZFK,K+", + Username = "root" + }); }); modelBuilder.Entity("WatchIt.Database.Model.Account.AccountProfilePicture", b => @@ -160,6 +177,11 @@ namespace WatchIt.Database.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("IsHistorical") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + b.Property("Name") .IsRequired() .HasMaxLength(100) @@ -176,11 +198,13 @@ namespace WatchIt.Database.Migrations new { Id = (short)1, + IsHistorical = false, Name = "Afghanistan" }, new { Id = (short)2, + IsHistorical = false, Name = "Albania" }); }); @@ -226,8 +250,7 @@ namespace WatchIt.Database.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); + .HasColumnType("text"); b.Property("Name") .IsRequired() @@ -272,14 +295,17 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("bigint"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Description") .HasMaxLength(1000) .HasColumnType("character varying(1000)"); - b.Property("Length") - .HasColumnType("interval"); + b.Property("Length") + .HasColumnType("smallint"); b.Property("MediaPosterImageId") .HasColumnType("uuid"); @@ -288,8 +314,8 @@ namespace WatchIt.Database.Migrations .HasMaxLength(250) .HasColumnType("character varying(250)"); - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone"); + b.Property("ReleaseDate") + .HasColumnType("date"); b.Property("Title") .IsRequired() @@ -325,11 +351,8 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("bigint"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("Budget") .HasColumnType("money"); @@ -432,13 +455,12 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("bigint"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("HasEnded") - .HasColumnType("boolean"); + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); b.HasKey("Id"); @@ -525,7 +547,7 @@ namespace WatchIt.Database.Migrations .HasMaxLength(200) .HasColumnType("character varying(200)"); - b.Property("GenderId") + b.Property("GenderId") .HasColumnType("smallint"); b.Property("Name") @@ -943,18 +965,6 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.Media", b => { - b.HasOne("WatchIt.Database.Model.Media.MediaMovie", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("WatchIt.Database.Model.Media.MediaSeries", null) - .WithOne("Media") - .HasForeignKey("WatchIt.Database.Model.Media.Media", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - b.HasOne("WatchIt.Database.Model.Media.MediaPosterImage", "MediaPosterImage") .WithOne("Media") .HasForeignKey("WatchIt.Database.Model.Media.Media", "MediaPosterImageId"); @@ -981,6 +991,17 @@ namespace WatchIt.Database.Migrations b.Navigation("Media"); }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithOne() + .HasForeignKey("WatchIt.Database.Model.Media.MediaMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPhotoImage", b => { b.HasOne("WatchIt.Database.Model.Media.Media", "Media") @@ -1011,6 +1032,17 @@ namespace WatchIt.Database.Migrations b.Navigation("Media"); }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => + { + b.HasOne("WatchIt.Database.Model.Media.Media", "Media") + .WithOne() + .HasForeignKey("WatchIt.Database.Model.Media.MediaSeries", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeriesEpisode", b => { b.HasOne("WatchIt.Database.Model.Media.MediaSeriesSeason", "MediaSeriesSeason") @@ -1037,9 +1069,7 @@ namespace WatchIt.Database.Migrations { b.HasOne("WatchIt.Database.Model.Common.Gender", "Gender") .WithMany() - .HasForeignKey("GenderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("GenderId"); b.HasOne("WatchIt.Database.Model.Person.PersonPhotoImage", "PersonPhoto") .WithOne("Person") @@ -1269,12 +1299,6 @@ namespace WatchIt.Database.Migrations b.Navigation("ViewCountsMedia"); }); - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaMovie", b => - { - b.Navigation("Media") - .IsRequired(); - }); - modelBuilder.Entity("WatchIt.Database.Model.Media.MediaPosterImage", b => { b.Navigation("Media") @@ -1283,9 +1307,6 @@ namespace WatchIt.Database.Migrations modelBuilder.Entity("WatchIt.Database.Model.Media.MediaSeries", b => { - b.Navigation("Media") - .IsRequired(); - b.Navigation("MediaSeriesSeasons"); }); diff --git a/WatchIt.Database/WatchIt.Database/WatchIt.Database.csproj b/WatchIt.Database/WatchIt.Database/WatchIt.Database.csproj index 49b2ef1..31e5072 100644 --- a/WatchIt.Database/WatchIt.Database/WatchIt.Database.csproj +++ b/WatchIt.Database/WatchIt.Database/WatchIt.Database.csproj @@ -1,31 +1,32 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + - - - + + + + diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs deleted file mode 100644 index 583f8d2..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateRequest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace WatchIt.Shared.Models.Accounts.Authenticate -{ - public class AuthenticateRequest - { - #region PROPERTIES - - [JsonPropertyName("username_or_email")] - public string UsernameOrEmail { get; set; } - - [JsonPropertyName("password")] - public string Password { get; set; } - - [JsonPropertyName("remember_me")] - public bool RememberMe { get; set; } - - #endregion - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs deleted file mode 100644 index 0bd0636..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Authenticate/AuthenticateResponse.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace WatchIt.Shared.Models.Accounts.Authenticate -{ - public class AuthenticateResponse - { - #region PROPERTIES - - [JsonPropertyName("access_token")] - public required string AccessToken { get; init; } - - [JsonPropertyName("refresh_token")] - public required string RefreshToken { get; init; } - - #endregion - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs deleted file mode 100644 index f1cb25d..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentValidation; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace WatchIt.Shared.Models.Accounts.Register -{ - public class RegisterRequest - { - #region PROPERTIES - - [JsonPropertyName("username")] - public string Username { get; set; } - - [JsonPropertyName("email")] - public string Email { get; set; } - - [JsonPropertyName("password")] - public string Password { get; set; } - - #endregion - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs b/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs deleted file mode 100644 index 43be31e..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/Accounts/Register/RegisterResponse.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using WatchIt.Database.Model.Account; - -namespace WatchIt.Shared.Models.Accounts.Register -{ - public class RegisterResponse - { - #region PROPERTIES - - [JsonPropertyName("id")] - public required long Id { get; init; } - - [JsonPropertyName("username")] - public required string Username { get; init; } - - [JsonPropertyName("email")] - public required string Email { get; init; } - - [JsonPropertyName("creation_date")] - public required DateTime CreationDate { get; init; } - - #endregion - - - - #region CONVERTION - - public static implicit operator RegisterResponse(Account account) => new RegisterResponse - { - Id = account.Id, - Username = account.Username, - Email = account.Email, - CreationDate = account.CreationDate - }; - - #endregion - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs b/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs deleted file mode 100644 index 7bab44e..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/RequestResult.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace WatchIt.Shared.Models -{ - public class RequestResult - { - #region PROPERTIES - - public RequestResultStatus Status { get; } - public IEnumerable ValidationMessages { get; init; } - - #endregion - - - - #region CONSTRUCTORS - - internal RequestResult(RequestResultStatus status) => Status = status; - - public static RequestResult Ok() => new RequestResult(RequestResultStatus.Ok); - public static RequestResult Ok() => new RequestResult(RequestResultStatus.Ok); - public static RequestResult Ok(T data) => new RequestResult(RequestResultStatus.Ok) { Data = data }; - public static RequestResult Created(string location, T resource) => new RequestResult(RequestResultStatus.Created) { NewResourceLocation = location, Data = resource }; - public static RequestResult NoContent() => new RequestResult(RequestResultStatus.NoContent); - public static RequestResult NoContent() => new RequestResult(RequestResultStatus.NoContent); - public static RequestResult BadRequest(params string[] validationErrors) => new RequestResult(RequestResultStatus.BadRequest) { ValidationMessages = validationErrors }; - public static RequestResult BadRequest(params string[] validationErrors) => new RequestResult(RequestResultStatus.BadRequest) { ValidationMessages = validationErrors }; - public static RequestResult Unauthorized(params string[] validationErrors) => new RequestResult(RequestResultStatus.Unauthorized) { ValidationMessages = validationErrors }; - public static RequestResult Unauthorized(params string[] validationErrors) => new RequestResult(RequestResultStatus.Unauthorized) { ValidationMessages = validationErrors }; - public static RequestResult Forbidden() => new RequestResult(RequestResultStatus.Forbidden); - public static RequestResult Forbidden() => new RequestResult(RequestResultStatus.Forbidden); - public static RequestResult NotFound() => new RequestResult(RequestResultStatus.NotFound); - public static RequestResult NotFound() => new RequestResult(RequestResultStatus.NotFound); - public static RequestResult Conflict() => new RequestResult(RequestResultStatus.Conflict); - public static RequestResult Conflict() => new RequestResult(RequestResultStatus.Conflict); - - #endregion - - - - #region CONVERSION - - public static implicit operator ActionResult(RequestResult result) => result.Status switch - { - RequestResultStatus.Ok => HandleOk(result), - RequestResultStatus.NoContent => HandleNoContent(), - RequestResultStatus.BadRequest => HandleBadRequest(result), - RequestResultStatus.Unauthorized => HandleUnauthorized(result), - RequestResultStatus.Forbidden => HandleForbidden(), - RequestResultStatus.NotFound => HandleNotFound(), - RequestResultStatus.Conflict => HandleConflict(), - }; - - protected static ActionResult HandleOk(RequestResult result) => new OkResult(); - protected static ActionResult HandleNoContent() => new NoContentResult(); - protected static ActionResult HandleBadRequest(RequestResult result) => new BadRequestObjectResult(result.ValidationMessages); - protected static ActionResult HandleUnauthorized(RequestResult result) => new UnauthorizedObjectResult(result.ValidationMessages); - protected static ActionResult HandleForbidden() => new ForbidResult(); - protected static ActionResult HandleNotFound() => new NotFoundResult(); - protected static ActionResult HandleConflict() => new ConflictResult(); - - #endregion - } - - public class RequestResult : RequestResult - { - #region PROPERTIES - - public T? Data { get; init; } - public string? NewResourceLocation { get; init; } - - #endregion - - - - #region CONSTRUCTORS - - internal RequestResult(RequestResultStatus type) : base(type) { } - - #endregion - - - - #region CONVERSION - - public static implicit operator ActionResult(RequestResult result) => result.Status switch - { - RequestResultStatus.Ok => HandleOk(result), - RequestResultStatus.Created => HandleCreated(result), - RequestResultStatus.NoContent => HandleNoContent(), - RequestResultStatus.BadRequest => HandleBadRequest(result), - RequestResultStatus.Unauthorized => HandleUnauthorized(result), - RequestResultStatus.Forbidden => HandleForbidden(), - RequestResultStatus.NotFound => HandleNotFound(), - RequestResultStatus.Conflict => HandleConflict(), - }; - - private static ActionResult HandleOk(RequestResult result) => result.Data is null ? new OkResult() : new OkObjectResult(result.Data); - private static ActionResult HandleCreated(RequestResult result) => new CreatedResult(result.NewResourceLocation, result.Data); - - #endregion - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs b/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs deleted file mode 100644 index 3808ca5..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/RequestResultStatus.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.Shared.Models -{ - public enum RequestResultStatus - { - Ok = 200, - Created = 201, - NoContent = 204, - BadRequest = 400, - Unauthorized = 401, - Forbidden = 403, - NotFound = 404, - Conflict = 409, - } -} diff --git a/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj b/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj deleted file mode 100644 index c5be2bb..0000000 --- a/WatchIt.Shared/WatchIt.Shared.Models/WatchIt.Shared.Models.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index a592f2e..53b1766 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -1,47 +1,33 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Shared.Models.Accounts.Authenticate; -using WatchIt.Shared.Models.Accounts.Register; -using WatchIt.WebAPI.Services.Controllers; +using WatchIt.Common.Model.Accounts; +using WatchIt.WebAPI.Services.Controllers.Accounts; -namespace WatchIt.WebAPI.Controllers +namespace WatchIt.WebAPI.Controllers; + +[ApiController] +[Route("accounts")] +public class AccountsController(IAccountsControllerService accountsControllerService) : ControllerBase { - [ApiController] - [Route("api/accounts")] - public class AccountsController(IAccountsControllerService accountsControllerService) : ControllerBase - { - #region METHODS - - [HttpPost] - [Route("register")] - [AllowAnonymous] - [ProducesResponseType(typeof(RegisterResponse), StatusCodes.Status201Created)] - [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public async Task Register([FromBody] RegisterRequest data) => await accountsControllerService.Register(data); - - [HttpPost] - [Route("authenticate")] - [AllowAnonymous] - [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task Authenticate([FromBody] AuthenticateRequest data) => await accountsControllerService.Authenticate(data); - - [HttpPost] - [Route("authenticate-refresh")] - [Authorize(AuthenticationSchemes = "refresh")] - [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task AuthenticateRefresh() => await accountsControllerService.AuthenticateRefresh(); - - - #endregion - } -} + [HttpPost("register")] + [AllowAnonymous] + [ProducesResponseType(typeof(RegisterResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task Register([FromBody]RegisterRequest body) => await accountsControllerService.Register(body); + + [HttpPost("authenticate")] + [AllowAnonymous] + [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + public async Task Authenticate([FromBody]AuthenticateRequest body) => await accountsControllerService.Authenticate(body); + + [HttpPost("authenticate-refresh")] + [Authorize(AuthenticationSchemes = "refresh")] + [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task AuthenticateRefresh() => await accountsControllerService.AuthenticateRefresh(); + +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/GenresController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/GenresController.cs new file mode 100644 index 0000000..5d48fbb --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/GenresController.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Model.Genres; +using WatchIt.WebAPI.Services.Controllers.Genres; + +namespace WatchIt.WebAPI.Controllers; + +[ApiController] +[Route("genres")] +public class GenresController(IGenresControllerService genresControllerService) : ControllerBase +{ + [HttpGet] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAll(GenreQueryParameters query) => await genresControllerService.GetAll(query); + + [HttpGet("{id}")] + [AllowAnonymous] + [ProducesResponseType(typeof(GenreResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get([FromRoute]short id) => await genresControllerService.Get(id); + + [HttpPost] + [Authorize] + [ProducesResponseType(typeof(GenreResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task Post([FromBody]GenreRequest body) => await genresControllerService.Post(body); + + [HttpPut("{id}")] + [Authorize] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Put([FromRoute]short id, [FromBody]GenreRequest body) => await genresControllerService.Put(id, body); + + [HttpDelete("{id}")] + [Authorize] + [ProducesResponseType(typeof(GenreResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Delete([FromRoute]short id) => await genresControllerService.Delete(id); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs new file mode 100644 index 0000000..3d56134 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Model.Genres; +using WatchIt.Common.Model.Movies; +using WatchIt.WebAPI.Services.Controllers.Movies; + +namespace WatchIt.WebAPI.Controllers; + +[ApiController] +[Route("movies")] +public class MoviesController(IMoviesControllerService moviesControllerService) : ControllerBase +{ + [HttpGet] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAll(MovieQueryParameters query) => await moviesControllerService.GetAll(query); + + [HttpGet("{id}")] + [AllowAnonymous] + [ProducesResponseType(typeof(MovieResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get([FromRoute]long id) => await moviesControllerService.Get(id); + + [HttpPost] + [Authorize] + [ProducesResponseType(typeof(MovieResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task Post([FromBody]MovieRequest body) => await moviesControllerService.Post(body); + + [HttpPut("{id}")] + [Authorize] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task Put([FromRoute]long id, [FromBody]MovieRequest body) => await moviesControllerService.Put(id, body); + + [HttpDelete("{id}")] + [Authorize] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task Delete([FromRoute]long id) => await moviesControllerService.Delete(id); + + [HttpGet("{id}/genres")] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetGenres([FromRoute]long id) => await moviesControllerService.GetGenres(id); + + [HttpPost("{id}/genres/{genre_id}")] + [Authorize] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task PostGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await moviesControllerService.PostGenre(id, genreId); + + [HttpDelete("{id}/genres/{genre_id}")] + [Authorize] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeleteGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await moviesControllerService.DeleteGenre(id, genreId); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj index 54ffa42..fecaed4 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj @@ -1,18 +1,20 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - - + + + - - - + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs deleted file mode 100644 index 7d98976..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/AccountsControllerService.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using SimpleToolkit.Extensions; -using System.Security.Cryptography; -using System.Text; -using WatchIt.Database; -using WatchIt.Database.Model.Account; -using WatchIt.Shared.Models; -using WatchIt.Shared.Models.Accounts.Authenticate; -using WatchIt.Shared.Models.Accounts.Register; -using WatchIt.WebAPI.Services.Utility.JWT; -using static System.Runtime.InteropServices.JavaScript.JSType; - -namespace WatchIt.WebAPI.Services.Controllers -{ - public interface IAccountsControllerService - { - Task> Register(RegisterRequest data); - Task> Authenticate(AuthenticateRequest data); - Task> AuthenticateRefresh(); - } - - public class AccountsControllerService(IJWTService jwtService, DatabaseContext database) : IAccountsControllerService - { - #region PUBLIC METHODS - - public async Task> Register(RegisterRequest data) - { - string leftSalt = StringExtensions.CreateRandom(20); - string rightSalt = StringExtensions.CreateRandom(20); - byte[] hash = ComputeHash(data.Password, leftSalt, rightSalt); - - Account account = new Account - { - Username = data.Username, - Email = data.Email, - Password = hash, - LeftSalt = leftSalt, - RightSalt = rightSalt - }; - await database.Accounts.AddAsync(account); - await database.SaveChangesAsync(); - - return RequestResult.Created($"accounts/{account.Id}", account); - } - - public async Task> Authenticate(AuthenticateRequest data) - { - Account? account = await database.Accounts.FirstOrDefaultAsync(x => string.Equals(x.Email, data.UsernameOrEmail) || string.Equals(x.Username, data.UsernameOrEmail)); - if (account is null) - { - return RequestResult.Unauthorized("User does not exists"); - } - - byte[] hash = ComputeHash(data.Password, account.LeftSalt, account.RightSalt); - if (!Enumerable.SequenceEqual(hash, account.Password)) - { - return RequestResult.Unauthorized("Incorrect password"); - } - - Task refreshTokenTask = jwtService.CreateRefreshToken(account, true); - Task accessTokenTask = jwtService.CreateAccessToken(account); - await Task.WhenAll(refreshTokenTask, accessTokenTask); - - AuthenticateResponse response = new AuthenticateResponse - { - AccessToken = accessTokenTask.Result, - RefreshToken = refreshTokenTask.Result, - }; - - return RequestResult.Ok(response); - } - - public async Task> AuthenticateRefresh() - { - - } - - #endregion - - - - #region PRIVATE METHODS - - protected byte[] ComputeHash(string password, string leftSalt, string rightSalt) => SHA512.Create().ComputeHash(Encoding.UTF8.GetBytes($"{leftSalt}{password}{rightSalt}")); - - #endregion - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs new file mode 100644 index 0000000..88963f9 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -0,0 +1,109 @@ +using System.Security.Cryptography; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using SimpleToolkit.Extensions; +using WatchIt.Common.Model.Accounts; +using WatchIt.Database; +using WatchIt.Database.Model.Account; +using WatchIt.WebAPI.Services.Controllers.Common; +using WatchIt.WebAPI.Services.Utility.Tokens; +using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions; +using WatchIt.WebAPI.Services.Utility.User; + +namespace WatchIt.WebAPI.Services.Controllers.Accounts; + +public class AccountsControllerService( + ILogger logger, + DatabaseContext database, + ITokensService tokensService, + IUserService userService +) : IAccountsControllerService +{ + #region PUBLIC METHODS + + public async Task Register(RegisterRequest data) + { + string leftSalt = StringExtensions.CreateRandom(20); + string rightSalt = StringExtensions.CreateRandom(20); + byte[] hash = ComputeHash(data.Password, leftSalt, rightSalt); + + Account account = new Account + { + Username = data.Username, + Email = data.Email, + Password = hash, + LeftSalt = leftSalt, + RightSalt = rightSalt, + }; + await database.Accounts.AddAsync(account); + await database.SaveChangesAsync(); + + logger.LogInformation($"New account with ID {account.Id} was created (username: {account.Username}; email: {account.Email})"); + return RequestResult.Created($"accounts/{account.Id}", new RegisterResponse(account)); + } + + public async Task Authenticate(AuthenticateRequest data) + { + Account? account = await database.Accounts.FirstOrDefaultAsync(x => string.Equals(x.Email, data.UsernameOrEmail) || string.Equals(x.Username, data.UsernameOrEmail)); + if (account is null || !ComputeHash(data.Password, account.LeftSalt, account.RightSalt).SequenceEqual(account.Password)) + { + return RequestResult.Unauthorized(); + } + + Task refreshTokenTask = tokensService.CreateRefreshTokenAsync(account, true); + Task accessTokenTask = tokensService.CreateAccessTokenAsync(account); + AuthenticateResponse response = new AuthenticateResponse + { + AccessToken = await accessTokenTask, + RefreshToken = await refreshTokenTask, + }; + + logger.LogInformation($"Account with ID {account.Id} was authenticated"); + return RequestResult.Ok(response); + } + + public async Task AuthenticateRefresh() + { + Guid jti = userService.GetJti(); + AccountRefreshToken? token = await database.AccountRefreshTokens.FirstOrDefaultAsync(x => x.Id == jti); + if (token is null || token.ExpirationDate < DateTime.UtcNow) + { + return RequestResult.Unauthorized(); + } + + AuthenticateResponse response; + try + { + Task refreshTokenTask = tokensService.ExtendRefreshTokenAsync(token.Account, token.Id); + Task accessTokenTask = tokensService.CreateAccessTokenAsync(token.Account); + response = new AuthenticateResponse + { + AccessToken = await accessTokenTask, + RefreshToken = await refreshTokenTask, + }; + } + catch (TokenNotFoundException) + { + return RequestResult.Unauthorized(); + } + catch (TokenNotExtendableException) + { + return RequestResult.Forbidden(); + } + + logger.LogInformation($"Account with ID {token.AccountId} was authenticated by token refreshing"); + return RequestResult.Ok(response); + } + + #endregion + + + + #region PRIVATE METHODS + + protected byte[] ComputeHash(string password, string leftSalt, string rightSalt) => SHA512.HashData(Encoding.UTF8.GetBytes($"{leftSalt}{password}{rightSalt}")); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs new file mode 100644 index 0000000..a9c5f61 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -0,0 +1,11 @@ +using WatchIt.Common.Model.Accounts; +using WatchIt.WebAPI.Services.Controllers.Common; + +namespace WatchIt.WebAPI.Services.Controllers.Accounts; + +public interface IAccountsControllerService +{ + Task Register(RegisterRequest data); + Task Authenticate(AuthenticateRequest data); + Task AuthenticateRefresh(); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/WatchIt.WebAPI.Services.Controllers.Accounts.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/WatchIt.WebAPI.Services.Controllers.Accounts.csproj new file mode 100644 index 0000000..63b4303 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/WatchIt.WebAPI.Services.Controllers.Accounts.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestBadRequestResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestBadRequestResult.cs new file mode 100644 index 0000000..2389416 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestBadRequestResult.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestBadRequestResult : RequestResult +{ + #region FIELDS + + private readonly ModelStateDictionary _modelState; + + #endregion + + + + #region CONSTRUCTORS + + public RequestBadRequestResult() : base(RequestResultStatus.BadRequest) + { + _modelState = new ModelStateDictionary(); + } + + #endregion + + + + #region PUBLIC METHODS + + public RequestBadRequestResult AddValidationError(string propertyName, string message) + { + _modelState.AddModelError(propertyName, message); + return this; + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new BadRequestObjectResult(_modelState); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestConflictResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestConflictResult.cs new file mode 100644 index 0000000..aa83308 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestConflictResult.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestConflictResult : RequestResult +{ + #region CONSTRUCTORS + + public RequestConflictResult() : base(RequestResultStatus.Conflict) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new ConflictResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestCreatedResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestCreatedResult.cs new file mode 100644 index 0000000..823aa10 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestCreatedResult.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestCreatedResult : RequestResult +{ + #region PROPERTIES + + public string Location { get; } + public T Data { get; } + + #endregion + + + + #region CONSTRUCTORS + + internal RequestCreatedResult(string location, T data) : base(RequestResultStatus.Created) + { + Location = location; + Data = data; + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new CreatedResult(Location, Data); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestForbiddenResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestForbiddenResult.cs new file mode 100644 index 0000000..a43e0b0 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestForbiddenResult.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestForbiddenResult : RequestResult +{ + #region CONSTRUCTORS + + public RequestForbiddenResult() : base(RequestResultStatus.Forbidden) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new ForbidResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNoContentResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNoContentResult.cs new file mode 100644 index 0000000..3c89c1f --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNoContentResult.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestNoContentResult : RequestResult +{ + #region CONSTRUCTORS + + internal RequestNoContentResult() : base(RequestResultStatus.NoContent) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new NoContentResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNotFoundResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNotFoundResult.cs new file mode 100644 index 0000000..c41927b --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestNotFoundResult.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestNotFoundResult : RequestResult +{ + #region CONSTRUCTORS + + public RequestNotFoundResult() : base(RequestResultStatus.NotFound) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new NotFoundResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestOkResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestOkResult.cs new file mode 100644 index 0000000..07ffb41 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestOkResult.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestOkResult : RequestResult +{ + #region CONSTRUCTORS + + internal RequestOkResult() : base(RequestResultStatus.Ok) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new OkResult(); + + #endregion +} + +public class RequestOkResult : RequestOkResult +{ + #region PROPERTIES + + public T Data { get; } + + #endregion + + + + #region CONSTRUCTORS + + internal RequestOkResult(T data) : base() => Data = data; + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new OkObjectResult(Data); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResult.cs new file mode 100644 index 0000000..07ebebb --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResult.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public abstract class RequestResult +{ + #region PROPERTIES + + public RequestResultStatus Status { get; } + + #endregion + + + + #region CONSTRUCTORS + + protected RequestResult(RequestResultStatus status) => Status = status; + + public static RequestOkResult Ok() => new RequestOkResult(); + public static RequestOkResult Ok(T data) => new RequestOkResult(data); + public static RequestCreatedResult Created(string location, T data) => new RequestCreatedResult(location, data); + public static RequestNoContentResult NoContent() => new RequestNoContentResult(); + public static RequestBadRequestResult BadRequest() => new RequestBadRequestResult(); + public static RequestUnauthorizedResult Unauthorized() => new RequestUnauthorizedResult(); + public static RequestForbiddenResult Forbidden() => new RequestForbiddenResult(); + public static RequestNotFoundResult NotFound() => new RequestNotFoundResult(); + public static RequestConflictResult Conflict() => new RequestConflictResult(); + + #endregion + + + + #region CONVERSION + + public static implicit operator ActionResult(RequestResult result) => result.ConvertToActionResult(); + + protected abstract ActionResult ConvertToActionResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResultStatus.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResultStatus.cs new file mode 100644 index 0000000..0d62a8e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestResultStatus.cs @@ -0,0 +1,13 @@ +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public enum RequestResultStatus +{ + Ok = 200, + Created = 201, + NoContent = 204, + BadRequest = 400, + Unauthorized = 401, + Forbidden = 403, + NotFound = 404, + Conflict = 409, +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestUnauthorizedResult.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestUnauthorizedResult.cs new file mode 100644 index 0000000..1765125 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/RequestUnauthorizedResult.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace WatchIt.WebAPI.Services.Controllers.Common; + +public class RequestUnauthorizedResult : RequestResult +{ + #region CONSTRUCTORS + + public RequestUnauthorizedResult() : base(RequestResultStatus.Unauthorized) + { + } + + #endregion + + + + #region CONVERTION + + protected override ActionResult ConvertToActionResult() => new UnauthorizedResult(); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/WatchIt.WebAPI.Services.Controllers.Common.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/WatchIt.WebAPI.Services.Controllers.Common.csproj new file mode 100644 index 0000000..bdbaf02 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Common/WatchIt.WebAPI.Services.Controllers.Common.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + ..\..\..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.2\Microsoft.AspNetCore.Mvc.Core.dll + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/GenresControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/GenresControllerService.cs new file mode 100644 index 0000000..5357aa8 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/GenresControllerService.cs @@ -0,0 +1,93 @@ +using Microsoft.EntityFrameworkCore; +using WatchIt.Common.Model.Genres; +using WatchIt.Database; +using WatchIt.Database.Model.Media; +using WatchIt.WebAPI.Services.Controllers.Common; +using WatchIt.WebAPI.Services.Utility.User; +using Genre = WatchIt.Database.Model.Common.Genre; + +namespace WatchIt.WebAPI.Services.Controllers.Genres; + +public class GenresControllerService(DatabaseContext database, IUserService userService) : IGenresControllerService +{ + #region PUBLIC METHODS + + public async Task GetAll(GenreQueryParameters query) + { + IEnumerable data = await database.Genres.Select(x => new GenreResponse(x)).ToListAsync(); + data = query.PrepareData(data); + return RequestResult.Ok(data); + } + + public async Task Get(short id) + { + Genre? item = await database.Genres.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + GenreResponse data = new GenreResponse(item); + return RequestResult.Ok(data); + } + + public async Task Post(GenreRequest data) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + Genre item = data.CreateGenre(); + await database.Genres.AddAsync(item); + await database.SaveChangesAsync(); + + return RequestResult.Created($"genres/{item.Id}", new GenreResponse(item)); + } + + public async Task Put(short id, GenreRequest data) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + Genre? item = await database.Genres.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + data.UpdateGenre(item); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task Delete(short id) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + Genre? item = await database.Genres.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + database.MediaGenres.AttachRange(item.MediaGenres); + database.MediaGenres.RemoveRange(item.MediaGenres); + database.Genres.Attach(item); + database.Genres.Remove(item); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/IGenresControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/IGenresControllerService.cs new file mode 100644 index 0000000..4fc50a9 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/IGenresControllerService.cs @@ -0,0 +1,13 @@ +using WatchIt.Common.Model.Genres; +using WatchIt.WebAPI.Services.Controllers.Common; + +namespace WatchIt.WebAPI.Services.Controllers.Genres; + +public interface IGenresControllerService +{ + Task GetAll(GenreQueryParameters query); + Task Get(short id); + Task Post(GenreRequest data); + Task Put(short id, GenreRequest data); + Task Delete(short id); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/WatchIt.WebAPI.Services.Controllers.Genres.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/WatchIt.WebAPI.Services.Controllers.Genres.csproj new file mode 100644 index 0000000..9e4603e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Genres/WatchIt.WebAPI.Services.Controllers.Genres.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/IMoviesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/IMoviesControllerService.cs new file mode 100644 index 0000000..4304808 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/IMoviesControllerService.cs @@ -0,0 +1,16 @@ +using WatchIt.Common.Model.Movies; +using WatchIt.WebAPI.Services.Controllers.Common; + +namespace WatchIt.WebAPI.Services.Controllers.Movies; + +public interface IMoviesControllerService +{ + Task GetAll(MovieQueryParameters query); + Task Get(long id); + Task Post(MovieRequest data); + Task Put(long id, MovieRequest data); + Task Delete(long id); + Task GetGenres(long movieId); + Task PostGenre(long movieId, short genreId); + Task DeleteGenre(long movieId, short genreId); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs new file mode 100644 index 0000000..84bbd3b --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs @@ -0,0 +1,171 @@ +using Microsoft.EntityFrameworkCore; +using WatchIt.Common.Model.Genres; +using WatchIt.Common.Model.Movies; +using WatchIt.Database; +using WatchIt.Database.Model.Media; +using WatchIt.WebAPI.Services.Controllers.Common; +using WatchIt.WebAPI.Services.Utility.User; +using Genre = WatchIt.Database.Model.Common.Genre; + +namespace WatchIt.WebAPI.Services.Controllers.Movies; + +public class MoviesControllerService(DatabaseContext database, IUserService userService) : IMoviesControllerService +{ + #region PUBLIC METHODS + + public async Task GetAll(MovieQueryParameters query) + { + IEnumerable data = await database.MediaMovies.Select(x => new MovieResponse(x)).ToListAsync(); + data = query.PrepareData(data); + return RequestResult.Ok(data); + } + + public async Task Get(long id) + { + MediaMovie? item = await database.MediaMovies.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + MovieResponse data = new MovieResponse(item); + return RequestResult.Ok(data); + } + + public async Task Post(MovieRequest data) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + Media mediaItem = data.CreateMedia(); + await database.Media.AddAsync(mediaItem); + await database.SaveChangesAsync(); + MediaMovie mediaMovieItem = data.CreateMediaMovie(mediaItem.Id); + await database.MediaMovies.AddAsync(mediaMovieItem); + await database.SaveChangesAsync(); + + return RequestResult.Created($"movies/{mediaItem.Id}", new MovieResponse(mediaMovieItem)); + } + + public async Task Put(long id, MovieRequest data) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaMovie? item = await database.MediaMovies.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + data.UpdateMediaMovie(item); + data.UpdateMedia(item.Media); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task Delete(long id) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaMovie? item = await database.MediaMovies.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + database.MediaMovies.Attach(item); + database.MediaMovies.Remove(item); + database.MediaPosterImages.Attach(item.Media.MediaPosterImage!); + database.MediaPosterImages.Remove(item.Media.MediaPosterImage!); + database.MediaPhotoImages.AttachRange(item.Media.MediaPhotoImages); + database.MediaPhotoImages.RemoveRange(item.Media.MediaPhotoImages); + database.MediaGenres.AttachRange(item.Media.MediaGenres); + database.MediaGenres.RemoveRange(item.Media.MediaGenres); + database.MediaProductionCountries.AttachRange(item.Media.MediaProductionCountries); + database.MediaProductionCountries.RemoveRange(item.Media.MediaProductionCountries); + database.PersonActorRoles.AttachRange(item.Media.PersonActorRoles); + database.PersonActorRoles.RemoveRange(item.Media.PersonActorRoles); + database.PersonCreatorRoles.AttachRange(item.Media.PersonCreatorRoles); + database.PersonCreatorRoles.RemoveRange(item.Media.PersonCreatorRoles); + database.RatingsMedia.AttachRange(item.Media.RatingMedia); + database.RatingsMedia.RemoveRange(item.Media.RatingMedia); + database.ViewCountsMedia.AttachRange(item.Media.ViewCountsMedia); + database.ViewCountsMedia.RemoveRange(item.Media.ViewCountsMedia); + database.Media.Attach(item.Media); + database.Media.Remove(item.Media); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task GetGenres(long movieId) + { + MediaMovie? item = await database.MediaMovies.FirstOrDefaultAsync(x => x.Id == movieId); + if (item is null) + { + return RequestResult.NotFound(); + } + + IEnumerable genres = item.Media.MediaGenres.Select(x => new GenreResponse(x.Genre)); + return RequestResult.Ok(genres); + } + + public async Task PostGenre(long movieId, short genreId) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaMovie? movieItem = await database.MediaMovies.FirstOrDefaultAsync(x => x.Id == movieId); + Genre? genreItem = await database.Genres.FirstOrDefaultAsync(x => x.Id == genreId); + if (movieItem is null || genreItem is null) + { + return RequestResult.NotFound(); + } + + await database.MediaGenres.AddAsync(new MediaGenre + { + GenreId = genreId, + MediaId = movieId, + }); + + return RequestResult.Ok(); + } + + public async Task DeleteGenre(long movieId, short genreId) + { + UserValidator validator = userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaGenre? item = await database.MediaGenres.FirstOrDefaultAsync(x => x.MediaId == movieId && x.GenreId == genreId); + if (item is null) + { + return RequestResult.NotFound(); + } + + database.MediaGenres.Attach(item); + database.MediaGenres.Remove(item); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/WatchIt.WebAPI.Services.Controllers.Movies.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/WatchIt.WebAPI.Services.Controllers.Movies.csproj new file mode 100644 index 0000000..9e4603e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/WatchIt.WebAPI.Services.Controllers.Movies.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj deleted file mode 100644 index 6c3b4fd..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs index 88d3f95..e8de848 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/ConfigurationService.cs @@ -1,26 +1,13 @@ using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WatchIt.WebAPI.Services.Utility.Configuration.Models; +using WatchIt.WebAPI.Services.Utility.Configuration.Model; -namespace WatchIt.WebAPI.Services.Utility.Configuration +namespace WatchIt.WebAPI.Services.Utility.Configuration; + +public class ConfigurationService(IConfiguration configuration) : IConfigurationService { - public interface IConfigurationService - { - ConfigurationData Data { get; } - } + #region PROPERTIES + public ConfigurationData Data => configuration.Get()!; - - public class ConfigurationService(IConfiguration configuration) : IConfigurationService - { - #region PROPERTIES - - public ConfigurationData Data => configuration.GetSection("WebAPI").Get()!; - - #endregion - } -} + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/IConfigurationService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/IConfigurationService.cs new file mode 100644 index 0000000..47dbadf --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/IConfigurationService.cs @@ -0,0 +1,8 @@ +using WatchIt.WebAPI.Services.Utility.Configuration.Model; + +namespace WatchIt.WebAPI.Services.Utility.Configuration; + +public interface IConfigurationService +{ + ConfigurationData Data { get; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Authentication.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Authentication.cs new file mode 100644 index 0000000..94da852 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Authentication.cs @@ -0,0 +1,8 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class Authentication +{ + public string Key { get; set; } + public string Issuer { get; set; } + public Tokens Tokens { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConfigurationData.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConfigurationData.cs new file mode 100644 index 0000000..d00b4e7 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConfigurationData.cs @@ -0,0 +1,10 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class ConfigurationData +{ + public Logging Logging { get; set; } + public string AllowedHosts { get; set; } + public ConnectionStrings ConnectionStrings { get; set; } + public RootUser RootUser { get; set; } + public Authentication Authentication { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConnectionStrings.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConnectionStrings.cs new file mode 100644 index 0000000..576839a --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/ConnectionStrings.cs @@ -0,0 +1,6 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class ConnectionStrings +{ + public string Default { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Console.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Console.cs new file mode 100644 index 0000000..c5975ce --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Console.cs @@ -0,0 +1,6 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class Console +{ + public FormatterOptions FormatterOptions { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/FormatterOptions.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/FormatterOptions.cs new file mode 100644 index 0000000..1fdc62d --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/FormatterOptions.cs @@ -0,0 +1,6 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class FormatterOptions +{ + public string TimestampFormat { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/LogLevel.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/LogLevel.cs new file mode 100644 index 0000000..267673e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/LogLevel.cs @@ -0,0 +1,8 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class LogLevel +{ + public string Default { get; set; } + public string Microsoft_AspNetCore { get; set; } + public string Microsoft_EntityFrameworkCore_Database_Command { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Logging.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Logging.cs new file mode 100644 index 0000000..b681b2c --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Logging.cs @@ -0,0 +1,7 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class Logging +{ + public LogLevel LogLevel { get; set; } + public Console Console { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/RootUser.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/RootUser.cs new file mode 100644 index 0000000..fe47fbe --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/RootUser.cs @@ -0,0 +1,8 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class RootUser +{ + public string Username { get; set; } + public string Email { get; set; } + public string Password { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Token.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Token.cs new file mode 100644 index 0000000..3a57eee --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Token.cs @@ -0,0 +1,7 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class Token +{ + public int NormalLifetime { get; set; } + public int ExtendedLifetime { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Tokens.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Tokens.cs new file mode 100644 index 0000000..bca6170 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Model/Tokens.cs @@ -0,0 +1,7 @@ +namespace WatchIt.WebAPI.Services.Utility.Configuration.Model; + +public class Tokens +{ + public Token RefreshToken { get; set; } + public Token AccessToken { get; set; } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs deleted file mode 100644 index 0b65457..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/AccessToken.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.Configuration.Models -{ - public class AccessToken - { - public int Lifetime { get; set; } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs deleted file mode 100644 index f68baf5..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/Authentication.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.Configuration.Models -{ - public class Authentication - { - public string Key { get; set; } - public string Issuer { get; set; } - public RefreshToken RefreshToken { get; set; } - public AccessToken AccessToken { get; set; } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs deleted file mode 100644 index faf1bec..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/ConfigurationData.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.Configuration.Models -{ - public class ConfigurationData - { - public Authentication Authentication { get; set; } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs deleted file mode 100644 index 542c115..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/Models/RefreshToken.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.Configuration.Models -{ - public class RefreshToken - { - public int Lifetime { get; set; } - public int ExtendedLifetime { get; set; } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj index 800b08f..d26bd3e 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Configuration/WatchIt.WebAPI.Services.Utility.Configuration.csproj @@ -1,13 +1,14 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs deleted file mode 100644 index e8afdc6..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/JWTService.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.Tokens; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database; -using WatchIt.Database.Model.Account; -using WatchIt.WebAPI.Services.Utility.Configuration; - -namespace WatchIt.WebAPI.Services.Utility.JWT -{ - public interface IJWTService - { - Task CreateAccessToken(Account account); - Task CreateRefreshToken(Account account, bool extendable); - Task ExtendRefreshToken(Account account, Guid id); - } - - - - public class JWTService(IConfigurationService configurationService, DatabaseContext database) : IJWTService - { - #region PUBLIC METHODS - - public async Task CreateRefreshToken(Account account, bool extendable) - { - int expirationMinutes = extendable ? configurationService.Data.Authentication.RefreshToken.ExtendedLifetime : configurationService.Data.Authentication.RefreshToken.Lifetime; - DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes); - Guid id = Guid.NewGuid(); - - AccountRefreshToken refreshToken = new AccountRefreshToken - { - Id = id, - AccountId = account.Id, - ExpirationDate = expirationDate, - IsExtendable = extendable - }; - database.AccountRefreshTokens.Add(refreshToken); - Task saveTask = database.SaveChangesAsync(); - - SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate); - tokenDescriptor.Audience = "refresh"; - tokenDescriptor.Subject.AddClaim(new Claim("extend", extendable.ToString())); - - string tokenString = TokenToString(tokenDescriptor); - - await saveTask; - - return tokenString; - } - - public async Task ExtendRefreshToken(Account account, Guid id) - { - AccountRefreshToken? token = account.AccountRefreshTokens.FirstOrDefault(x => x.Id == id); - if (token is null) - { - throw new TokenNotFoundException(); - } - if (!token.IsExtendable) - { - throw new TokenNotExtendableException(); - } - - int expirationMinutes = configurationService.Data.Authentication.RefreshToken.ExtendedLifetime; - DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes); - - token.ExpirationDate = expirationDate; - - Task saveTask = database.SaveChangesAsync(); - - SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate); - tokenDescriptor.Audience = "refresh"; - tokenDescriptor.Subject.AddClaim(new Claim("extend", bool.TrueString)); - - string tokenString = TokenToString(tokenDescriptor); - - await saveTask; - - return tokenString; - } - - public async Task CreateAccessToken(Account account) - { - DateTime lifetime = DateTime.Now.AddMinutes(configurationService.Data.Authentication.AccessToken.Lifetime); - Guid id = Guid.NewGuid(); - - SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, lifetime); - tokenDescriptor.Audience = "access"; - tokenDescriptor.Subject.AddClaim(new Claim("admin", account.IsAdmin.ToString())); - - return TokenToString(tokenDescriptor); - } - - #endregion - - - - #region PRIVATE METHODS - - protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime) => new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(new List - { - new Claim(JwtRegisteredClaimNames.Jti, id.ToString()), - new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()), - new Claim(JwtRegisteredClaimNames.Email, account.Email), - new Claim(JwtRegisteredClaimNames.UniqueName, account.Username), - new Claim(JwtRegisteredClaimNames.Exp, expirationTime.ToString()), - }), - Expires = expirationTime, - Issuer = configurationService.Data.Authentication.Issuer, - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512) - }; - - protected string TokenToString(SecurityTokenDescriptor tokenDescriptor) - { - System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); - handler.InboundClaimTypeMap.Clear(); - - SecurityToken token = handler.CreateToken(tokenDescriptor); - - return handler.WriteToken(token); - } - - #endregion - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs deleted file mode 100644 index 971cce2..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotExtendableException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.JWT -{ - public class TokenNotExtendableException : Exception - { - public TokenNotExtendableException() : base() { } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs deleted file mode 100644 index 1e37897..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/TokenNotFoundException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WatchIt.WebAPI.Services.Utility.JWT -{ - public class TokenNotFoundException : Exception - { - public TokenNotFoundException() : base() { } - } -} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj deleted file mode 100644 index e3ca5ed..0000000 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.JWT/WatchIt.WebAPI.Services.Utility.JWT.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotExtendableException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotExtendableException.cs new file mode 100644 index 0000000..b890f6a --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotExtendableException.cs @@ -0,0 +1,6 @@ +namespace WatchIt.WebAPI.Services.Utility.Tokens.Exceptions; + +public class TokenNotExtendableException : Exception +{ + public TokenNotExtendableException() : base() { } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotFoundException.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotFoundException.cs new file mode 100644 index 0000000..07980ae --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/Exceptions/TokenNotFoundException.cs @@ -0,0 +1,6 @@ +namespace WatchIt.WebAPI.Services.Utility.Tokens.Exceptions; + +public class TokenNotFoundException : Exception +{ + public TokenNotFoundException() : base() { } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/ITokensService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/ITokensService.cs new file mode 100644 index 0000000..3d699bb --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/ITokensService.cs @@ -0,0 +1,11 @@ +using WatchIt.Database.Model.Account; + +namespace WatchIt.WebAPI.Services.Utility.Tokens; + +public interface ITokensService +{ + Task CreateRefreshTokenAsync(Account account, bool extendable); + Task ExtendRefreshTokenAsync(Account account, Guid id); + Task CreateAccessTokenAsync(Account account); + string CreateAccessToken(Account account); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/TokensService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/TokensService.cs new file mode 100644 index 0000000..7182a15 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/TokensService.cs @@ -0,0 +1,115 @@ +using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Microsoft.IdentityModel.Tokens; +using WatchIt.Database; +using WatchIt.Database.Model.Account; +using WatchIt.WebAPI.Services.Utility.Configuration; +using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions; + +namespace WatchIt.WebAPI.Services.Utility.Tokens; + +public class TokensService(DatabaseContext database, IConfigurationService configurationService) : ITokensService +{ + #region FIELDS + + private readonly Configuration.Model.Tokens _tokensConfig = configurationService.Data.Authentication.Tokens; + + #endregion + + + + #region PUBLIC METHODS + + public async Task CreateRefreshTokenAsync(Account account, bool extendable) + { + int expirationMinutes = extendable ? _tokensConfig.RefreshToken.ExtendedLifetime : _tokensConfig.RefreshToken.NormalLifetime; + DateTime expirationDate = DateTime.UtcNow.AddMinutes(expirationMinutes); + Guid id = Guid.NewGuid(); + + database.AccountRefreshTokens.Add(new AccountRefreshToken + { + Id = id, + AccountId = account.Id, + ExpirationDate = expirationDate, + IsExtendable = extendable, + }); + await database.SaveChangesAsync(); + + return GenerateRefreshJwt(account, id, expirationDate, extendable); + } + + public async Task ExtendRefreshTokenAsync(Account account, Guid id) + { + AccountRefreshToken? token = account.AccountRefreshTokens.FirstOrDefault(x => x.Id == id); + switch (token) + { + case null: throw new TokenNotFoundException(); + case { IsExtendable: true }: throw new TokenNotExtendableException(); + } + + DateTime expirationDate = DateTime.UtcNow.AddMinutes(_tokensConfig.RefreshToken.ExtendedLifetime); + + token.ExpirationDate = expirationDate; + await database.SaveChangesAsync(); + + return GenerateRefreshJwt(account, id, expirationDate, true); + } + + public async Task CreateAccessTokenAsync(Account account) => await Task.Run(() => CreateAccessToken(account)); + + public string CreateAccessToken(Account account) + { + DateTime lifetime = DateTime.Now.AddMinutes(_tokensConfig.AccessToken.NormalLifetime); + Guid id = Guid.NewGuid(); + + SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, lifetime); + tokenDescriptor.Audience = "access"; + + return TokenToString(tokenDescriptor); + } + + #endregion + + + + #region PRIVATE METHODS + + protected string GenerateRefreshJwt(Account account, Guid id, DateTime expirationDate, bool extendable) + { + SecurityTokenDescriptor tokenDescriptor = CreateBaseSecurityTokenDescriptor(account, id, expirationDate); + tokenDescriptor.Audience = "refresh"; + tokenDescriptor.Subject.AddClaim(new Claim("extend", extendable.ToString())); + + return TokenToString(tokenDescriptor); + } + + protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime) => new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new List + { + new Claim(JwtRegisteredClaimNames.Jti, id.ToString()), + new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()), + new Claim(JwtRegisteredClaimNames.Email, account.Email), + new Claim(JwtRegisteredClaimNames.UniqueName, account.Username), + new Claim(JwtRegisteredClaimNames.Exp, expirationTime.Ticks.ToString()), + new Claim("admin", account.IsAdmin.ToString()), + }), + Expires = expirationTime, + Issuer = configurationService.Data.Authentication.Issuer, + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512) + }; + + protected string TokenToString(SecurityTokenDescriptor tokenDescriptor) + { + JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); + handler.InboundClaimTypeMap.Clear(); + + SecurityToken token = handler.CreateToken(tokenDescriptor); + + return handler.WriteToken(token); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/WatchIt.WebAPI.Services.Utility.Tokens.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/WatchIt.WebAPI.Services.Utility.Tokens.csproj new file mode 100644 index 0000000..d5346c1 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.Tokens/WatchIt.WebAPI.Services.Utility.Tokens.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs index e0c8cd9..8fdaa8c 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/UserService.cs @@ -1,18 +1,36 @@ -using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.AspNetCore.Http; +using WatchIt.Database; -namespace WatchIt.WebAPI.Services.Utility.User +namespace WatchIt.WebAPI.Services.Utility.User; + +public class UserService(DatabaseContext database, IHttpContextAccessor accessor) : IUserService { - public class UserService(IHttpContextAccessor accessor) + #region PUBLIC METHODS + + public ClaimsPrincipal GetRawUser() { - #region PUBLIC METHODS - - - - #endregion + if (accessor.HttpContext is null) + { + throw new NullReferenceException(); + } + return accessor.HttpContext.User; } -} + + public UserValidator GetValidator() + { + ClaimsPrincipal rawUser = GetRawUser(); + return new UserValidator(database, rawUser); + } + + public Guid GetJti() + { + ClaimsPrincipal user = GetRawUser(); + Claim jtiClaim = user.FindFirst(JwtRegisteredClaimNames.Jti)!; + Guid guid = Guid.Parse(jtiClaim.Value); + return guid; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj index 297920e..6a8d36e 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Utility/WatchIt.WebAPI.Services.Utility.User/WatchIt.WebAPI.Services.Utility.User.csproj @@ -1,13 +1,18 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs index 7f2bdfb..f14c417 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AuthenticateRequestValidator.cs @@ -1,26 +1,13 @@ using FluentValidation; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using WatchIt.Database; -using WatchIt.Shared.Models.Accounts.Authenticate; +using WatchIt.Common.Model.Accounts; -namespace WatchIt.WebAPI.Validators.Accounts +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AuthenticateRequestValidator : AbstractValidator { - public class AuthenticateRequestValidator : AbstractValidator + public AuthenticateRequestValidator() { - #region CONSTRUCTOR - - public AuthenticateRequestValidator(DatabaseContext database) - { - RuleFor(x => x.UsernameOrEmail).NotEmpty(); - RuleFor(x => x.Password).NotEmpty(); - } - - #endregion + RuleFor(x => x.UsernameOrEmail).NotEmpty(); + RuleFor(x => x.Password).NotEmpty(); } -} +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs index ea90618..a0ed98b 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/RegisterRequestValidator.cs @@ -1,31 +1,21 @@ using FluentValidation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using WatchIt.Common.Model.Accounts; using WatchIt.Database; -using WatchIt.Shared.Models.Accounts.Register; -namespace WatchIt.WebAPI.Validators.Accounts +namespace WatchIt.WebAPI.Validators.Accounts; + +public class RegisterRequestValidator : AbstractValidator { - public class RegisterRequestValidator : AbstractValidator + public RegisterRequestValidator(DatabaseContext database) { - #region CONSTRUCTOR - - public RegisterRequestValidator(DatabaseContext database) - { - RuleFor(x => x.Username).MinimumLength(5) - .MaximumLength(50) - .CannotBeIn(database.Accounts, x => x.Username).WithMessage("Username was already used"); - RuleFor(x => x.Email).EmailAddress() - .CannotBeIn(database.Accounts, x => x.Email).WithMessage("Email was already used"); - RuleFor(x => x.Password).MinimumLength(8) - .Must(x => x.Any(c => Char.IsUpper(c))).WithMessage("Password must contain at least one uppercase letter.") - .Must(x => x.Any(c => Char.IsLower(c))).WithMessage("Password must contain at least one lowercase letter.") - .Must(x => x.Any(c => Char.IsDigit(c))).WithMessage("Password must contain at least one digit."); - } - - #endregion + RuleFor(x => x.Username).MinimumLength(5) + .MaximumLength(50) + .CannotBeIn(database.Accounts, x => x.Username).WithMessage("Username was already used"); + RuleFor(x => x.Email).EmailAddress() + .CannotBeIn(database.Accounts, x => x.Email).WithMessage("Email was already used"); + RuleFor(x => x.Password).MinimumLength(8) + .Must(x => x.Any(char.IsUpper)).WithMessage("Password must contain at least one uppercase letter.") + .Must(x => x.Any(char.IsLower)).WithMessage("Password must contain at least one lowercase letter.") + .Must(x => x.Any(char.IsDigit)).WithMessage("Password must contain at least one digit."); } -} +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs index 76d157c..d243004 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/CustomValidators.cs @@ -1,18 +1,11 @@ using FluentValidation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace WatchIt.WebAPI.Validators +namespace WatchIt.WebAPI.Validators; + +public static class CustomValidators { - public static class CustomValidators - { - public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => !collection.Any(e => Equals(e, x))); - public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => !collection.Select(propertyFunc).Any(e => Equals(e, x))); - public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => collection.Any(e => Equals(e, x))); - public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => collection.Select(propertyFunc).Any(e => Equals(e, x))); - - } -} + public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => !collection.Any(e => Equals(e, x))); + public static IRuleBuilderOptions CannotBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => !collection.Select(propertyFunc).Any(e => Equals(e, x))); + public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection) => ruleBuilder.Must(x => collection.Any(e => Equals(e, x))); + public static IRuleBuilderOptions MustBeIn(this IRuleBuilder ruleBuilder, IEnumerable collection, Func propertyFunc) => ruleBuilder.Must(x => collection.Select(propertyFunc).Any(e => Equals(e, x))); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Genres/GenreRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Genres/GenreRequestValidator.cs new file mode 100644 index 0000000..cf5a9b9 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Genres/GenreRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using WatchIt.Common.Model.Genres; +using WatchIt.Database; + +namespace WatchIt.WebAPI.Validators.Genres; + +public class GenreRequestValidator : AbstractValidator +{ + public GenreRequestValidator() + { + RuleFor(x => x.Name).MaximumLength(100); + When(x => !string.IsNullOrWhiteSpace(x.Description), () => RuleFor(x => x.Description).MaximumLength(1000)); + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj index 4498da0..84b3d21 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/WatchIt.WebAPI.Validators.csproj @@ -1,19 +1,19 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - - + + + + - - - - + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/DeleteExpiredRefreshTokensService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/DeleteExpiredRefreshTokensService.cs new file mode 100644 index 0000000..7c43b54 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/DeleteExpiredRefreshTokensService.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using WatchIt.Database; +using WatchIt.Database.Model.Account; + +namespace WatchIt.WebAPI.WorkerServices; + +public class DeleteExpiredRefreshTokensService(ILogger logger, DatabaseContext database) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + + Task delayTask = Task.Delay(300000, stoppingToken); + Task actionTask = Action(); + + await Task.WhenAll(delayTask, actionTask); + } + } + + protected async Task Action() + { + IEnumerable tokens = database.AccountRefreshTokens.Where(x => x.ExpirationDate < DateTime.UtcNow); + database.AccountRefreshTokens.AttachRange(tokens); + database.AccountRefreshTokens.RemoveRange(tokens); + await database.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/WatchIt.WebAPI.WorkerServices.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/WatchIt.WebAPI.WorkerServices.csproj new file mode 100644 index 0000000..5f17238 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.WorkerServices/WatchIt.WebAPI.WorkerServices.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/Dockerfile b/WatchIt.WebAPI/WatchIt.WebAPI/Dockerfile new file mode 100644 index 0000000..7485107 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI/Dockerfile @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER $APP_UID +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj", "WatchIt.WebAPI/WatchIt.WebAPI/"] +RUN dotnet restore "WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj" +COPY . . +WORKDIR "/src/WatchIt.WebAPI/WatchIt.WebAPI" +RUN dotnet build "WatchIt.WebAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "WatchIt.WebAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "WatchIt.WebAPI.dll"] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs new file mode 100644 index 0000000..1bd5391 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs @@ -0,0 +1,161 @@ +using System.Reflection; +using System.Text; +using FluentValidation; +using FluentValidation.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using WatchIt.Database; +using WatchIt.WebAPI.Services.Controllers.Accounts; +using WatchIt.WebAPI.Services.Controllers.Genres; +using WatchIt.WebAPI.Services.Controllers.Movies; +using WatchIt.WebAPI.Services.Utility.Configuration; +using WatchIt.WebAPI.Services.Utility.Tokens; +using WatchIt.WebAPI.Services.Utility.User; +using WatchIt.WebAPI.Validators; +using WatchIt.WebAPI.WorkerServices; + +namespace WatchIt.WebAPI; + +public static class Program +{ + #region PUBLIC METHODS + + public static void Main(string[] args) + { + WebApplication app = WebApplication.CreateBuilder(args) + .SetupAuthentication() + .SetupDatabase() + .SetupWorkerServices() + .SetupServices() + .SetupApplication() + .Build(); + + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + app.MapControllers(); + + app.Run(); + } + + #endregion + + + + #region PRIVATE METHODS + + private static WebApplicationBuilder SetupAuthentication(this WebApplicationBuilder builder) + { + AuthenticationBuilder authenticationBuilder = builder.Services.AddAuthentication(x => + { + x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }); + authenticationBuilder.AddJwtBearer(x => + { + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetValue("Authentication:Key")!)), + ValidateAudience = true, + ValidAudience = "access", + ValidIssuer = builder.Configuration.GetValue("Authentication:Issuer"), + ValidateLifetime = true, + ClockSkew = TimeSpan.FromMinutes(1), + }; + x.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Append("Token-Expired", "true"); + } + + return Task.CompletedTask; + }, + }; + }); + authenticationBuilder.AddJwtBearer("refresh", x => + { + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetValue("Authentication:Key")!)), + ValidateAudience = true, + ValidIssuer = builder.Configuration.GetValue("Authentication:Issuer"), + ValidAudience = "refresh", + ValidateLifetime = true, + ClockSkew = TimeSpan.FromMinutes(1) + }; + x.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Append("Token-Expired", "true"); + } + return Task.CompletedTask; + } + }; + }); + builder.Services.AddAuthorization(); + + return builder; + } + + private static WebApplicationBuilder SetupDatabase(this WebApplicationBuilder builder) + { + builder.Services.AddDbContext(x => x.UseLazyLoadingProxies().UseNpgsql(builder.Configuration.GetConnectionString("Default")), ServiceLifetime.Singleton); + return builder; + } + + private static WebApplicationBuilder SetupWorkerServices(this WebApplicationBuilder builder) + { + builder.Services.AddHostedService(); + return builder; + } + + private static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder) + { + // Utility + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + // Controller + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + return builder; + } + + private static WebApplicationBuilder SetupApplication(this WebApplicationBuilder builder) + { + builder.Services.AddValidatorsFromAssembly(Assembly.GetAssembly(typeof(CustomValidators))); + builder.Services.AddFluentValidationAutoValidation(); + builder.Services.AddHttpContextAccessor(); + builder.Services.AddControllers(); + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + return builder; + } + + #endregion +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/Properties/launchSettings.json b/WatchIt.WebAPI/WatchIt.WebAPI/Properties/launchSettings.json new file mode 100644 index 0000000..22794a2 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:10207", + "sslPort": 44335 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5179", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7160;http://localhost:5179", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj b/WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj new file mode 100644 index 0000000..2cabcac --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI/WatchIt.WebAPI.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + Linux + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + .dockerignore + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/appsettings.json b/WatchIt.WebAPI/WatchIt.WebAPI/appsettings.json new file mode 100644 index 0000000..d416db6 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI/appsettings.json @@ -0,0 +1,36 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Warning" + }, + "Console": { + "FormatterOptions": { + "TimestampFormat": "[yyyy-MM-dd HH:mm:ss] " + } + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "Default": "Host=localhost;Database=watchit;Username=watchit;Password=Xdv2Etchavbuuho" + }, + "RootUser": { + "Username": "root", + "Email": "root@watch.it", + "Password": "bECdHfbus2QHr4QQjApM" + }, + "Authentication": { + "Key": "testkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytest", + "Issuer": "WatchIt", + "Tokens": { + "RefreshToken": { + "NormalLifetime": 1440, + "ExtendedLifetime": 10080 + }, + "AccessToken": { + "NormalLifetime": 5 + } + } + } +} diff --git a/WatchIt.Website/WatchIt.Website/Components/App.razor b/WatchIt.Website/WatchIt.Website/Components/App.razor new file mode 100644 index 0000000..b0f6aea --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/App.razor @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor b/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..df0c2d6 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor @@ -0,0 +1,23 @@ +@inherits LayoutComponentBase + +
+ + +
+
+ About +
+ +
+ @Body +
+
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor.css b/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..038baf1 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Layout/MainLayout.razor.css @@ -0,0 +1,96 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; + } + + .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; + } + + .top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row { + justify-content: space-between; + } + + .top-row ::deep a, .top-row ::deep .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row.auth ::deep a:first-child { + flex: 1; + text-align: right; + width: 0; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor b/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor new file mode 100644 index 0000000..5305774 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor.css b/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor.css new file mode 100644 index 0000000..4e15395 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Layout/NavMenu.razor.css @@ -0,0 +1,105 @@ +.navbar-toggler { + appearance: none; + cursor: pointer; + width: 3.5rem; + height: 2.5rem; + color: white; + position: absolute; + top: 0.5rem; + right: 1rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); +} + +.navbar-toggler:checked { + background-color: rgba(255, 255, 255, 0.5); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.bi { + display: inline-block; + position: relative; + width: 1.25rem; + height: 1.25rem; + margin-right: 0.75rem; + top: -1px; + background-size: cover; +} + +.bi-house-door-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); +} + +.bi-plus-square-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); +} + +.bi-list-nested-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep .nav-link { + color: #d7d7d7; + background: none; + border: none; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + width: 100%; + } + +.nav-item ::deep a.active { + background-color: rgba(255,255,255,0.37); + color: white; +} + +.nav-item ::deep .nav-link:hover { + background-color: rgba(255,255,255,0.1); + color: white; +} + +.nav-scrollable { + display: none; +} + +.navbar-toggler:checked ~ .nav-scrollable { + display: block; +} + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .nav-scrollable { + /* Never collapse the sidebar for wide screens */ + display: block; + + /* Allow sidebar to scroll for tall menus */ + height: calc(100vh - 3.5rem); + overflow-y: auto; + } +} diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Counter.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Counter.razor new file mode 100644 index 0000000..372905f --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/Counter.razor @@ -0,0 +1,19 @@ +@page "/counter" + +Counter + +

Counter

+ +

Current count: @currentCount

+ + + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } + +} \ No newline at end of file diff --git a/WatchIt/Website/Pages/Error.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor similarity index 93% rename from WatchIt/Website/Pages/Error.razor rename to WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor index 576cc2d..9d7c6be 100644 --- a/WatchIt/Website/Pages/Error.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor @@ -25,12 +25,12 @@

@code{ - [CascadingParameter] - private HttpContext? HttpContext { get; set; } + [CascadingParameter] private HttpContext? HttpContext { get; set; } private string? RequestId { get; set; } private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); protected override void OnInitialized() => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; -} + +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor new file mode 100644 index 0000000..dfcdf75 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor @@ -0,0 +1,7 @@ +@page "/" + +Home + +

Hello, world!

+ +Welcome to your new app. \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor new file mode 100644 index 0000000..570e371 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor @@ -0,0 +1,66 @@ +@page "/weather" + +Weather + +

Weather

+ +

This component demonstrates showing data.

+ +@if (forecasts == null) +{ +

+ Loading... +

+} +else +{ + + + + + + + + + + + @foreach (var forecast in forecasts) + { + + + + + + + } + +
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
+} + +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() + { + // Simulate asynchronous loading to demonstrate a loading indicator + await Task.Delay(500); + + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).ToArray(); + } + + private class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } + +} \ No newline at end of file diff --git a/WatchIt/Website/Routes.razor b/WatchIt.Website/WatchIt.Website/Components/Routes.razor similarity index 62% rename from WatchIt/Website/Routes.razor rename to WatchIt.Website/WatchIt.Website/Components/Routes.razor index f756e19..ae94e9e 100644 --- a/WatchIt/Website/Routes.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Routes.razor @@ -1,6 +1,6 @@  - - + + - + \ No newline at end of file diff --git a/WatchIt/Website/_Imports.razor b/WatchIt.Website/WatchIt.Website/Components/_Imports.razor similarity index 91% rename from WatchIt/Website/_Imports.razor rename to WatchIt.Website/WatchIt.Website/Components/_Imports.razor index aea964c..6433b1a 100644 --- a/WatchIt/Website/_Imports.razor +++ b/WatchIt.Website/WatchIt.Website/Components/_Imports.razor @@ -6,5 +6,5 @@ @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop -@using WatchIt @using WatchIt.Website +@using WatchIt.Website.Components \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Dockerfile b/WatchIt.Website/WatchIt.Website/Dockerfile new file mode 100644 index 0000000..1c2192d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Dockerfile @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER $APP_UID +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj", "WatchIt.Website/WatchIt.Website/"] +RUN dotnet restore "WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj" +COPY . . +WORKDIR "/src/WatchIt.Website/WatchIt.Website" +RUN dotnet build "WatchIt.Website.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "WatchIt.Website.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "WatchIt.Website.dll"] diff --git a/WatchIt.Website/WatchIt.Website/Program.cs b/WatchIt.Website/WatchIt.Website/Program.cs new file mode 100644 index 0000000..63d2902 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Program.cs @@ -0,0 +1,35 @@ +using WatchIt.Website.Components; + +namespace WatchIt.Website; + +public class Program +{ + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (!app.Environment.IsDevelopment()) + { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseHttpsRedirection(); + + app.UseStaticFiles(); + app.UseAntiforgery(); + + app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + + app.Run(); + } +} \ No newline at end of file diff --git a/WatchIt/Properties/launchSettings.json b/WatchIt.Website/WatchIt.Website/Properties/launchSettings.json similarity index 80% rename from WatchIt/Properties/launchSettings.json rename to WatchIt.Website/WatchIt.Website/Properties/launchSettings.json index 1aae778..33d2d6c 100644 --- a/WatchIt/Properties/launchSettings.json +++ b/WatchIt.Website/WatchIt.Website/Properties/launchSettings.json @@ -4,8 +4,8 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:35148", - "sslPort": 44373 + "applicationUrl": "http://localhost:44254", + "sslPort": 44356 } }, "profiles": { @@ -13,7 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:5125", + "applicationUrl": "http://localhost:5209", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -22,7 +22,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:7228;http://localhost:5125", + "applicationUrl": "https://localhost:7111;http://localhost:5209", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj new file mode 100644 index 0000000..6012379 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + Linux + + + + + .dockerignore + + + + diff --git a/WatchIt/appsettings.Development.json b/WatchIt.Website/WatchIt.Website/appsettings.json similarity index 80% rename from WatchIt/appsettings.Development.json rename to WatchIt.Website/WatchIt.Website/appsettings.json index 0c208ae..10f68b8 100644 --- a/WatchIt/appsettings.Development.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -4,5 +4,6 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/WatchIt/wwwroot/app.css b/WatchIt.Website/WatchIt.Website/wwwroot/app.css similarity index 83% rename from WatchIt/wwwroot/app.css rename to WatchIt.Website/WatchIt.Website/wwwroot/app.css index e398853..2bd9b78 100644 --- a/WatchIt/wwwroot/app.css +++ b/WatchIt.Website/WatchIt.Website/wwwroot/app.css @@ -1,3 +1,25 @@ +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #006bb7; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +} + +.content { + padding-top: 1.1rem; +} + h1:focus { outline: none; } diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css b/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css new file mode 100644 index 0000000..02ae65b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css @@ -0,0 +1,7 @@ +@charset "UTF-8";/*! + * Bootstrap v5.1.0 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-rgb:33,37,41;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite}@-webkit-keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css.map b/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css.map new file mode 100644 index 0000000..afcd9e3 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/wwwroot/bootstrap/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap v5.1.0 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n\n// scss-docs-start import-stack\n// Configuration\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"utilities\";\n\n// Layout & components\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"containers\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"accordion\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"alert\";\n@import \"progress\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"offcanvas\";\n@import \"placeholders\";\n\n// Helpers\n@import \"helpers\";\n\n// Utilities\n@import \"utilities/api\";\n// scss-docs-end import-stack\n",":root {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$variable-prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$variable-prefix}#{$color}-rgb: #{$value};\n }\n\n --#{$variable-prefix}white-rgb: #{to-rgb($white)};\n --#{$variable-prefix}black-rgb: #{to-rgb($black)};\n --#{$variable-prefix}body-rgb: #{to-rgb($body-color)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$variable-prefix}gradient: #{$gradient};\n\n // Root and body\n // stylelint-disable custom-property-empty-line-before\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$variable-prefix}root-font-size: #{$font-size-root};\n }\n --#{$variable-prefix}body-font-family: #{$font-family-base};\n --#{$variable-prefix}body-font-size: #{$font-size-base};\n --#{$variable-prefix}body-font-weight: #{$font-weight-base};\n --#{$variable-prefix}body-line-height: #{$line-height-base};\n --#{$variable-prefix}body-color: #{$body-color};\n @if $body-text-align != null {\n --#{$variable-prefix}body-text-align: #{$body-text-align};\n }\n --#{$variable-prefix}body-bg: #{$body-bg};\n // scss-docs-end root-body-variables\n // stylelint-enable custom-property-empty-line-before\n}\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n font-size: var(--#{$variable-prefix}-root-font-size);\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$variable-prefix}body-font-family);\n @include font-size(var(--#{$variable-prefix}body-font-size));\n font-weight: var(--#{$variable-prefix}body-font-weight);\n line-height: var(--#{$variable-prefix}body-line-height);\n color: var(--#{$variable-prefix}body-color);\n text-align: var(--#{$variable-prefix}body-text-align);\n background-color: var(--#{$variable-prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n background-color: currentColor;\n border: 0;\n opacity: $hr-opacity;\n}\n\nhr:not([size]) {\n height: $hr-height; // 2\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-` - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } - -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor deleted file mode 100644 index 9d7c6be..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/Error.razor +++ /dev/null @@ -1,36 +0,0 @@ -@page "/Error" -@using System.Diagnostics - -Error - -

Error.

-

An error occurred while processing your request.

- -@if (ShowRequestId) -{ -

- Request ID: @RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

- -@code{ - [CascadingParameter] private HttpContext? HttpContext { get; set; } - - private string? RequestId { get; set; } - private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - protected override void OnInitialized() => - RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; - -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor deleted file mode 100644 index dfcdf75..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/Home.razor +++ /dev/null @@ -1,7 +0,0 @@ -@page "/" - -Home - -

Hello, world!

- -Welcome to your new app. \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor deleted file mode 100644 index 570e371..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/Weather.razor +++ /dev/null @@ -1,66 +0,0 @@ -@page "/weather" - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

- Loading... -

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate a loading indicator - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } - -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor b/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor new file mode 100644 index 0000000..53a4e95 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor @@ -0,0 +1,3 @@ +@inherits LayoutComponentBase + +@Body \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css b/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor new file mode 100644 index 0000000..2b3dc7c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor @@ -0,0 +1,99 @@ +@using System.Diagnostics +@using System.Text +@using WatchIt.Common.Model.Media +@using WatchIt.Website.Services.WebAPI.Media +@inherits LayoutComponentBase + +@if (loaded) +{ +
+
+ +
+

Menu

+
+
+
+ @if (signedIn) + { +

test

+ } + else + { + Sign in or up + } +
+
+
+
+
+ @Body +
+
+
+ + +} + + + + +@code +{ + #region SERVICES + + [Inject] public ILogger Logger { get; set; } = default!; + [Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!; + + #endregion + + + + #region FIELDS + + private bool loaded = false; + + private string background = "assets/background_temp.jpg"; + private string firstGradientColor = "#c6721c"; + private string secondGradientColor = "#85200c"; + private bool signedIn = false; + + #endregion + + + + #region METHODS + + protected override async Task OnInitializedAsync() + { + Action backgroundSuccess = (data) => + { + string imageBase64 = Convert.ToBase64String(data.Image); + string firstColor = BitConverter.ToString(data.Background.FirstGradientColor) + .Replace("-", string.Empty); + string secondColor = BitConverter.ToString(data.Background.SecondGradientColor) + .Replace("-", string.Empty); + + background = $"data:{data.MimeType};base64,{imageBase64}"; + firstGradientColor = $"#{firstColor}"; + secondGradientColor = $"#{secondColor}"; + }; + await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess, null); + + loaded = true; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css new file mode 100644 index 0000000..350d80d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css @@ -0,0 +1,21 @@ +body { + background-size: cover; +} + +.logo { + font-size: 40px; +} + +.header { + position: sticky; + top: 10px; + height: 60px; +} + +.body-content { + padding-top: 100px; +} + +h1 { + margin: 0; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/Auth.razor b/WatchIt.Website/WatchIt.Website/Pages/Auth.razor new file mode 100644 index 0000000..ab08b93 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/Auth.razor @@ -0,0 +1,73 @@ +@page "/auth" +@layout EmptyLayout + +WatchIt - @(_authType == AuthType.SignIn ? "Sign in" : "Sign up") + + + + + +
+
+ + @if (_authType == AuthType.SignIn) + { + + } +
+
+ + + + + + + + + + + +@code { + + #region ENUMS + + private enum AuthType + { + SignIn, + SignUp + } + + #endregion + + + + #region FIELDS + + private AuthType _authType = AuthType.SignIn; + private string _background = "assets/background_temp.jpg"; + private string _firstGradientColor = "#c6721c"; + private string _secondGradientColor = "#85200c"; + + #endregion + + + + #region METHODS + + protected override Task OnInitializedAsync() + { + return base.OnInitializedAsync(); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/Auth.razor.css b/WatchIt.Website/WatchIt.Website/Pages/Auth.razor.css new file mode 100644 index 0000000..93f6b3c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/Auth.razor.css @@ -0,0 +1,8 @@ +body { + background-size: cover; +} + +.logo { + font-size: 60px; + margin: 10px; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/Home.razor b/WatchIt.Website/WatchIt.Website/Pages/Home.razor new file mode 100644 index 0000000..f68e886 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/Home.razor @@ -0,0 +1,50 @@ +@page "/" + +WatchIt + +
+
+
+

Hello, world!

+ + Welcome to your new app. + + +

Hello, world!

+ + Welcome to your new app. + + +

Hello, world!

+ + Welcome to your new app. + +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. +

Hello, world!

+ + Welcome to your new app. + + +

Hello, world!

+ + Welcome to your new app. +
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Program.cs b/WatchIt.Website/WatchIt.Website/Program.cs index 63d2902..e3647e1 100644 --- a/WatchIt.Website/WatchIt.Website/Program.cs +++ b/WatchIt.Website/WatchIt.Website/Program.cs @@ -1,18 +1,20 @@ -using WatchIt.Website.Components; +using WatchIt.Common.Services.HttpClient; +using WatchIt.Website.Services.Utility.Configuration; +using WatchIt.Website.Services.WebAPI.Accounts; +using WatchIt.Website.Services.WebAPI.Media; namespace WatchIt.Website; -public class Program +public static class Program { + #region PUBLIC METHODS + public static void Main(string[] args) { - var builder = WebApplication.CreateBuilder(args); - - // Add services to the container. - builder.Services.AddRazorComponents() - .AddInteractiveServerComponents(); - - var app = builder.Build(); + WebApplication app = WebApplication.CreateBuilder(args) + .SetupServices() + .SetupApplication() + .Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) @@ -32,4 +34,34 @@ public class Program app.Run(); } + + #endregion + + + + #region PRIVATE METHODS + + private static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder) + { + builder.Services.AddHttpClient(); + + // Utility + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + // WebAPI + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + return builder; + } + + private static WebApplicationBuilder SetupApplication(this WebApplicationBuilder builder) + { + builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); + return builder; + } + + #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Routes.razor b/WatchIt.Website/WatchIt.Website/Routes.razor similarity index 100% rename from WatchIt.Website/WatchIt.Website/Components/Routes.razor rename to WatchIt.Website/WatchIt.Website/Routes.razor diff --git a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj index 6012379..68af502 100644 --- a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj +++ b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj @@ -13,4 +13,30 @@
+ + + + + + + + + <_ContentIncludedByDefault Remove="Components\Layout\EmptyLayout.razor" /> + <_ContentIncludedByDefault Remove="Components\Layout\MainLayout.razor" /> + <_ContentIncludedByDefault Remove="Components\Pages\Counter.razor" /> + <_ContentIncludedByDefault Remove="Components\Pages\Error.razor" /> + <_ContentIncludedByDefault Remove="Components\Pages\Home.razor" /> + <_ContentIncludedByDefault Remove="Components\Pages\Weather.razor" /> + + + + + + + + + + + + diff --git a/WatchIt.Website/WatchIt.Website/Components/_Imports.razor b/WatchIt.Website/WatchIt.Website/_Imports.razor similarity index 91% rename from WatchIt.Website/WatchIt.Website/Components/_Imports.razor rename to WatchIt.Website/WatchIt.Website/_Imports.razor index 6433b1a..60530df 100644 --- a/WatchIt.Website/WatchIt.Website/Components/_Imports.razor +++ b/WatchIt.Website/WatchIt.Website/_Imports.razor @@ -7,4 +7,4 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using WatchIt.Website -@using WatchIt.Website.Components \ No newline at end of file +@using WatchIt.Website.Layout \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 10f68b8..47244bc 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -5,5 +5,46 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Endpoints": { + "Base": "https://localhost:7160", + "Accounts": { + "Base": "/accounts", + "Register": "/register", + "Authenticate": "/authenticate", + "AuthenticateRefresh": "/authenticate-refresh" + }, + "Genres": { + "Base": "/genres", + "GetAll": "", + "Get": "/{0}", + "Post": "", + "Put": "/{0}", + "Delete": "/{0}" + }, + "Movies": { + "Base": "/movies", + "GetAll": "", + "Get": "/{0}", + "Post": "", + "Put": "/{0}", + "Delete": "/{0}", + "GetGenres": "/{0}/genres", + "PostGenre": "{0}/genres/{1}", + "DeleteGenre": "{0}/genres/{1}" + }, + "Media": { + "Base": "/media", + "GetGenres": "/{0}/genres", + "PostGenre": "/{0}/genres/{1}", + "DeleteGenre": "/{0}/genres/{1}", + "GetPhotoMediaRandomBackground": "/{0}/photos/random_background", + "GetPhoto": "/photos/{0}", + "GetPhotos": "/photos", + "GetPhotoRandomBackground": "/photos/random_background", + "PostPhoto": "/photos", + "PutPhoto": "/photos/{0}", + "DeletePhoto": "/photos/{0}" + } + } } diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/app.css b/WatchIt.Website/WatchIt.Website/wwwroot/app.css index 2bd9b78..09abd8f 100644 --- a/WatchIt.Website/WatchIt.Website/wwwroot/app.css +++ b/WatchIt.Website/WatchIt.Website/wwwroot/app.css @@ -1,51 +1,80 @@ -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +@font-face { + font-family: Belanosima; + src: url(fonts/Belanosima-Regular.ttf) format('truetype'); } -a, .btn-link { - color: #006bb7; +@font-face { + font-family: Belanosima; + src: url(fonts/Belanosima-Bold.ttf) format('truetype'); + font-weight: bold; } -.btn-primary { +body, html { + background-color: transparent; + height: 100%; + margin: 0; + padding: 0; +} + +.logo { + font-family: Belanosima; + text-decoration: none; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.panel { + background-color: rgba(0, 0, 0, 0.8); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(25px); + z-index: 1000; +} + +.main-button { + --r:10px; + --b:2px; + + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + color: transparent; + padding: 5px 10px; + + border-radius: var(--r); + display: block; + align-items: self-end; + position: relative; + z-index:0; + text-decoration: none; + transition: 0.3s; + font-family: Belanosima; +} + +.main-button::before { + content:""; + position:absolute; + z-index:-1; + inset: 0; + border: var(--b) solid transparent; + border-radius: var(--r); + background: inherit; + background-origin: border-box; + background-clip: border-box; + -webkit-mask: + linear-gradient(#fff 0 0) padding-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + -webkit-mask-repeat: no-repeat; +} + +.main-button:hover { color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; + -webkit-text-fill-color: #fff; + -webkit-background-clip: border-box; + background-clip: border-box; } -.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; -} - -.content { - padding-top: 1.1rem; -} - -h1:focus { - outline: none; -} - -.valid.modified:not([type=checkbox]) { - outline: 1px solid #26b050; -} - -.invalid { - outline: 1px solid #e50000; -} - -.validation-message { - color: #e50000; -} - -.blazor-error-boundary { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; - padding: 1rem 1rem 1rem 3.7rem; - color: white; -} - - .blazor-error-boundary::after { - content: "An error has occurred." - } - -.darker-border-checkbox.form-check-input { - border-color: #929292; -} +.main-button:hover::before { + -webkit-mask:none; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/background_temp.jpg b/WatchIt.Website/WatchIt.Website/wwwroot/assets/background_temp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d0e86cec39f955377ed8ec3d7fbfb36e43eba212 GIT binary patch literal 180726 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 BPJ;jd literal 0 HcmV?d00001 diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/fonts/Belanosima-Bold.ttf b/WatchIt.Website/WatchIt.Website/wwwroot/fonts/Belanosima-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9e2bdb97175bdf6d6382603767b4d355ca4ffadd GIT binary patch literal 39888 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 z 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 osdN?nkkK-uT>g%;?o0GKQR literal 0 HcmV?d00001 diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/fonts/Belanosima-SemiBold.ttf b/WatchIt.Website/WatchIt.Website/wwwroot/fonts/Belanosima-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..317f0a8e9590599e56b046eac6e11d880c0a8484 GIT binary patch literal 38476 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 W%G~$r|8XBP^!3je{_8I?_x*3Kgj09` literal 0 HcmV?d00001 diff --git a/WatchIt.sln b/WatchIt.sln index 09ee577..863c78e 100644 --- a/WatchIt.sln +++ b/WatchIt.sln @@ -50,6 +50,32 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website", "WatchIt. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Movies", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Movies\WatchIt.WebAPI.Services.Controllers.Movies.csproj", "{69BB6A9E-B673-42AB-A516-6B2513E21FDC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website.Services", "WatchIt.Website.Services", "{A82972D0-9A60-4B3F-AE46-9F304D79137F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website.Services.WebAPI", "WatchIt.Website.Services.WebAPI", "{46E3711F-18BD-4004-AF53-EA4D8643D92F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Accounts", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Accounts\WatchIt.Website.Services.WebAPI.Accounts.csproj", "{68B7E892-9074-4034-8AFC-2474D7D5BE29}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Genres", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Genres\WatchIt.Website.Services.WebAPI.Genres.csproj", "{A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Movies", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Movies\WatchIt.Website.Services.WebAPI.Movies.csproj", "{539404EB-BDFD-46F8-8F21-6A231ABED9B1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website.Services.Utility", "WatchIt.Website.Services.Utility", "{130BC8F5-82CE-4EDF-AECB-21594DD41849}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Common.Services", "WatchIt.Common.Services", "{882A9795-4AC0-4556-9750-6582B2701EFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Query", "WatchIt.Common\WatchIt.Common.Query\WatchIt.Common.Query.csproj", "{6C3AE7B4-18C5-42D3-B254-460027E50143}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Services.HttpClient", "WatchIt.Common\WatchIt.Common.Services\WatchIt.Common.Services.HttpClient\WatchIt.Common.Services.HttpClient.csproj", "{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Utility.Configuration", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Utility.Configuration\WatchIt.Website.Services.Utility.Configuration.csproj", "{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Media", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Media\WatchIt.WebAPI.Services.Controllers.Media.csproj", "{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Media", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Media\WatchIt.Website.Services.WebAPI.Media.csproj", "{1D64B7B5-650D-4AF3-AC33-A8D1F0999906}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Common", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Common\WatchIt.Website.Services.WebAPI.Common.csproj", "{2D62ED42-489E-4888-9479-E5A50A0E7D70}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +103,19 @@ Global {2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} {46CE78A1-3EC3-4112-AAAD-26EEB8D8B194} = {4CB91BF6-87F1-4088-A943-62548CD1F9F4} {69BB6A9E-B673-42AB-A516-6B2513E21FDC} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} + {A82972D0-9A60-4B3F-AE46-9F304D79137F} = {4CB91BF6-87F1-4088-A943-62548CD1F9F4} + {46E3711F-18BD-4004-AF53-EA4D8643D92F} = {A82972D0-9A60-4B3F-AE46-9F304D79137F} + {68B7E892-9074-4034-8AFC-2474D7D5BE29} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} + {A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} + {539404EB-BDFD-46F8-8F21-6A231ABED9B1} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} + {130BC8F5-82CE-4EDF-AECB-21594DD41849} = {A82972D0-9A60-4B3F-AE46-9F304D79137F} + {882A9795-4AC0-4556-9750-6582B2701EFA} = {E98C42C1-26E5-4939-8C22-72C253DE874B} + {6C3AE7B4-18C5-42D3-B254-460027E50143} = {E98C42C1-26E5-4939-8C22-72C253DE874B} + {A4A75CCA-0DEE-4F1E-9816-60674CA807FA} = {882A9795-4AC0-4556-9750-6582B2701EFA} + {0DBBE7EA-05FE-481F-8814-6FA0BC9E571F} = {130BC8F5-82CE-4EDF-AECB-21594DD41849} + {3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} + {1D64B7B5-650D-4AF3-AC33-A8D1F0999906} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} + {2D62ED42-489E-4888-9479-E5A50A0E7D70} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23383776-1F27-4B5D-8C7C-57BFF75FA473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -147,5 +186,41 @@ Global {69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU {69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU {69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Release|Any CPU.Build.0 = Release|Any CPU + {68B7E892-9074-4034-8AFC-2474D7D5BE29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68B7E892-9074-4034-8AFC-2474D7D5BE29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68B7E892-9074-4034-8AFC-2474D7D5BE29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68B7E892-9074-4034-8AFC-2474D7D5BE29}.Release|Any CPU.Build.0 = Release|Any CPU + {A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A98D06A6-9C95-4449-9F4E-1D31BBE1D9B1}.Release|Any CPU.Build.0 = Release|Any CPU + {539404EB-BDFD-46F8-8F21-6A231ABED9B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {539404EB-BDFD-46F8-8F21-6A231ABED9B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {539404EB-BDFD-46F8-8F21-6A231ABED9B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {539404EB-BDFD-46F8-8F21-6A231ABED9B1}.Release|Any CPU.Build.0 = Release|Any CPU + {6C3AE7B4-18C5-42D3-B254-460027E50143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C3AE7B4-18C5-42D3-B254-460027E50143}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C3AE7B4-18C5-42D3-B254-460027E50143}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C3AE7B4-18C5-42D3-B254-460027E50143}.Release|Any CPU.Build.0 = Release|Any CPU + {A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Release|Any CPU.Build.0 = Release|Any CPU + {0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Release|Any CPU.Build.0 = Release|Any CPU + {3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Release|Any CPU.Build.0 = Release|Any CPU + {1D64B7B5-650D-4AF3-AC33-A8D1F0999906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D64B7B5-650D-4AF3-AC33-A8D1F0999906}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D64B7B5-650D-4AF3-AC33-A8D1F0999906}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D64B7B5-650D-4AF3-AC33-A8D1F0999906}.Release|Any CPU.Build.0 = Release|Any CPU + {2D62ED42-489E-4888-9479-E5A50A0E7D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D62ED42-489E-4888-9479-E5A50A0E7D70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D62ED42-489E-4888-9479-E5A50A0E7D70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D62ED42-489E-4888-9479-E5A50A0E7D70}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From eed8dd5997943c44e0b22e376d37d5b9820e4d70 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 3 Jul 2024 22:21:51 +0200 Subject: [PATCH 5/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21a189c..3871bbb 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# JustWatchIt \ No newline at end of file +# WatchIt