From dfdc2ec1090967e595cdad8d0ce0e3b842af9b57 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:28:24 +0300 Subject: [PATCH 001/126] feat: add interface for check configured settings --- Endpoint/Configuration/General/Interfaces/IIsConfigured.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Endpoint/Configuration/General/Interfaces/IIsConfigured.cs diff --git a/Endpoint/Configuration/General/Interfaces/IIsConfigured.cs b/Endpoint/Configuration/General/Interfaces/IIsConfigured.cs new file mode 100644 index 0000000..60c09e0 --- /dev/null +++ b/Endpoint/Configuration/General/Interfaces/IIsConfigured.cs @@ -0,0 +1,6 @@ +namespace Mirea.Api.Endpoint.Configuration.General.Interfaces; + +public interface IIsConfigured +{ + bool IsConfigured(); +} \ No newline at end of file From c06ed8b4794d986d578af49627b97a3a665b82dc Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:30:42 +0300 Subject: [PATCH 002/126] refactor: move files --- Endpoint/Configuration/{ => Swagger}/ConfigureSwaggerOptions.cs | 2 +- Endpoint/Configuration/{ => Swagger}/SwaggerDefaultValues.cs | 2 +- Endpoint/Program.cs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename Endpoint/Configuration/{ => Swagger}/ConfigureSwaggerOptions.cs (96%) rename Endpoint/Configuration/{ => Swagger}/SwaggerDefaultValues.cs (97%) diff --git a/Endpoint/Configuration/ConfigureSwaggerOptions.cs b/Endpoint/Configuration/Swagger/ConfigureSwaggerOptions.cs similarity index 96% rename from Endpoint/Configuration/ConfigureSwaggerOptions.cs rename to Endpoint/Configuration/Swagger/ConfigureSwaggerOptions.cs index 39280bb..bf47325 100644 --- a/Endpoint/Configuration/ConfigureSwaggerOptions.cs +++ b/Endpoint/Configuration/Swagger/ConfigureSwaggerOptions.cs @@ -5,7 +5,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System; -namespace Mirea.Api.Endpoint.Configuration; +namespace Mirea.Api.Endpoint.Configuration.Swagger; public class ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) : IConfigureOptions { diff --git a/Endpoint/Configuration/SwaggerDefaultValues.cs b/Endpoint/Configuration/Swagger/SwaggerDefaultValues.cs similarity index 97% rename from Endpoint/Configuration/SwaggerDefaultValues.cs rename to Endpoint/Configuration/Swagger/SwaggerDefaultValues.cs index cefb307..581393e 100644 --- a/Endpoint/Configuration/SwaggerDefaultValues.cs +++ b/Endpoint/Configuration/Swagger/SwaggerDefaultValues.cs @@ -6,7 +6,7 @@ using System; using System.Linq; using System.Text.Json; -namespace Mirea.Api.Endpoint.Configuration; +namespace Mirea.Api.Endpoint.Configuration.Swagger; public class SwaggerDefaultValues : IOperationFilter { diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 3607500..4674bc9 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -10,6 +10,7 @@ using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Properties; +using Mirea.Api.Endpoint.Configuration.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections; From 41689bca7fa711716cbee3f39abee24f8b65a694 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:36:18 +0300 Subject: [PATCH 003/126] feat: add attribute for required configuration --- .../General/Attributes/RequiredSettingsAttribute.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Endpoint/Configuration/General/Attributes/RequiredSettingsAttribute.cs diff --git a/Endpoint/Configuration/General/Attributes/RequiredSettingsAttribute.cs b/Endpoint/Configuration/General/Attributes/RequiredSettingsAttribute.cs new file mode 100644 index 0000000..dcb13ac --- /dev/null +++ b/Endpoint/Configuration/General/Attributes/RequiredSettingsAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Mirea.Api.Endpoint.Configuration.General.Attributes; + +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public class RequiredSettingsAttribute : Attribute; + +// todo: only with IIsConfigured. If possible add Roslyn Analyzer later \ No newline at end of file From 266e66a35c7ed05fe09ba34ef70116b53e46af85 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:38:24 +0300 Subject: [PATCH 004/126] feat: expand the configuration functionality --- .../Configuration/General/GeneralConfig.cs | 14 ++++++ .../General/Settings/CacheSettings.cs | 23 ++++++++++ .../General/Settings/DbSettings.cs | 22 +++++++++ .../General/Settings/EmailSettings.cs | 23 ++++++++++ .../General/Settings/LogSettings.cs | 19 ++++++++ .../General/Settings/ScheduleSettings.cs | 45 +++++++++++++++++++ Endpoint/Properties/Settings.cs | 36 --------------- 7 files changed, 146 insertions(+), 36 deletions(-) create mode 100644 Endpoint/Configuration/General/GeneralConfig.cs create mode 100644 Endpoint/Configuration/General/Settings/CacheSettings.cs create mode 100644 Endpoint/Configuration/General/Settings/DbSettings.cs create mode 100644 Endpoint/Configuration/General/Settings/EmailSettings.cs create mode 100644 Endpoint/Configuration/General/Settings/LogSettings.cs create mode 100644 Endpoint/Configuration/General/Settings/ScheduleSettings.cs delete mode 100644 Endpoint/Properties/Settings.cs diff --git a/Endpoint/Configuration/General/GeneralConfig.cs b/Endpoint/Configuration/General/GeneralConfig.cs new file mode 100644 index 0000000..60f98a1 --- /dev/null +++ b/Endpoint/Configuration/General/GeneralConfig.cs @@ -0,0 +1,14 @@ +using Mirea.Api.Endpoint.Configuration.General.Settings; + +namespace Mirea.Api.Endpoint.Configuration.General; + +public class GeneralConfig +{ + public const string FilePath = "Settings.json"; + + public DbSettings? DbSettings { get; set; } + public CacheSettings? CacheSettings { get; set; } + public ScheduleSettings? ScheduleSettings { get; set; } + public EmailSettings? EmailSettings { get; set; } + public LogSettings? LogSettings { get; set; } +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/Settings/CacheSettings.cs b/Endpoint/Configuration/General/Settings/CacheSettings.cs new file mode 100644 index 0000000..a0a7802 --- /dev/null +++ b/Endpoint/Configuration/General/Settings/CacheSettings.cs @@ -0,0 +1,23 @@ +using Mirea.Api.Endpoint.Configuration.General.Attributes; +using Mirea.Api.Endpoint.Configuration.General.Interfaces; + +namespace Mirea.Api.Endpoint.Configuration.General.Settings; + +[RequiredSettings] +public class CacheSettings : IIsConfigured +{ + public enum CacheEnum + { + Memcached, + Redis + } + + public CacheEnum TypeDatabase { get; set; } + public string? ConnectionString { get; set; } + + public bool IsConfigured() + { + return TypeDatabase == CacheEnum.Memcached || + !string.IsNullOrEmpty(ConnectionString); + } +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/Settings/DbSettings.cs b/Endpoint/Configuration/General/Settings/DbSettings.cs new file mode 100644 index 0000000..f8ae35e --- /dev/null +++ b/Endpoint/Configuration/General/Settings/DbSettings.cs @@ -0,0 +1,22 @@ +using Mirea.Api.Endpoint.Configuration.General.Attributes; +using Mirea.Api.Endpoint.Configuration.General.Interfaces; + +namespace Mirea.Api.Endpoint.Configuration.General.Settings; + +[RequiredSettings] +public class DbSettings : IIsConfigured +{ + public enum DatabaseEnum + { + Mysql, + Sqlite, + PostgresSql + } + public DatabaseEnum TypeDatabase { get; set; } + public required string ConnectionStringSql { get; set; } + + public bool IsConfigured() + { + return !string.IsNullOrEmpty(ConnectionStringSql); + } +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/Settings/EmailSettings.cs b/Endpoint/Configuration/General/Settings/EmailSettings.cs new file mode 100644 index 0000000..bdd5179 --- /dev/null +++ b/Endpoint/Configuration/General/Settings/EmailSettings.cs @@ -0,0 +1,23 @@ +using Mirea.Api.Endpoint.Configuration.General.Interfaces; + +namespace Mirea.Api.Endpoint.Configuration.General.Settings; + +public class EmailSettings : IIsConfigured +{ + public string? Server { get; set; } + public string? User { get; set; } + public string? Password { get; set; } + public string? From { get; set; } + public int? Port { get; set; } + public bool? Ssl { get; set; } + + public bool IsConfigured() + { + return !string.IsNullOrEmpty(Server) && + !string.IsNullOrEmpty(User) && + !string.IsNullOrEmpty(Password) && + !string.IsNullOrEmpty(From) && + Port.HasValue && + Ssl.HasValue; + } +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/Settings/LogSettings.cs b/Endpoint/Configuration/General/Settings/LogSettings.cs new file mode 100644 index 0000000..4a42d28 --- /dev/null +++ b/Endpoint/Configuration/General/Settings/LogSettings.cs @@ -0,0 +1,19 @@ +using Mirea.Api.Endpoint.Configuration.General.Attributes; +using Mirea.Api.Endpoint.Configuration.General.Interfaces; + +namespace Mirea.Api.Endpoint.Configuration.General.Settings; + +[RequiredSettings] +public class LogSettings : IIsConfigured +{ + public bool EnableLogToFile { get; set; } + public string? LogFilePath { get; set; } + public string? LogFileName { get; set; } + + public bool IsConfigured() + { + return !EnableLogToFile || + !string.IsNullOrEmpty(LogFilePath) && + !string.IsNullOrEmpty(LogFileName); + } +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/Settings/ScheduleSettings.cs b/Endpoint/Configuration/General/Settings/ScheduleSettings.cs new file mode 100644 index 0000000..a45b41f --- /dev/null +++ b/Endpoint/Configuration/General/Settings/ScheduleSettings.cs @@ -0,0 +1,45 @@ +using Mirea.Api.Endpoint.Configuration.General.Attributes; +using Mirea.Api.Endpoint.Configuration.General.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Mirea.Api.Endpoint.Configuration.General.Settings; + +[RequiredSettings] +public class ScheduleSettings : IIsConfigured +{ + public struct PairPeriodTime + { + public TimeOnly Start { get; set; } + public TimeOnly End { get; set; } + + public PairPeriodTime(TimeOnly t1, TimeOnly t2) + { + if (t1 > t2) + { + Start = t2; + End = t1; + } + else + { + Start = t1; + End = t2; + } + } + + public PairPeriodTime(Dto.Common.PairPeriodTime time) : this(time.Start, time.End) { } + } + + public required string CronUpdateSchedule { get; set; } + public DateOnly StartTerm { get; set; } + public required IDictionary PairPeriod { get; set; } + + public bool IsConfigured() + { + return !string.IsNullOrEmpty(CronUpdateSchedule) && + StartTerm != default && + PairPeriod.Count != 0 && + PairPeriod.Any(); + } +} \ No newline at end of file diff --git a/Endpoint/Properties/Settings.cs b/Endpoint/Properties/Settings.cs deleted file mode 100644 index 1bee892..0000000 --- a/Endpoint/Properties/Settings.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Mirea.Api.DataAccess.Persistence.Properties; - -namespace Mirea.Api.Endpoint.Properties; - -public class EmailSettings -{ - public string? Server { get; set; } - public string? User { get; set; } - public string? Password { get; set; } - public string? From { get; set; } - public int? Port { get; set; } - public bool? Ssl { get; set; } -} - -public class LogSettings -{ - public bool EnableLogToFile { get; set; } - public string? LogFilePath { get; set; } - public string? LogFileName { get; set; } -} - -public class ScheduleSettings -{ - // Every 6 hours - public string CronUpdateSchedule { get; set; } = "0 0 0/6 * * *"; -} - -public class Settings -{ - public const string FilePath = "Settings.json"; - - public EmailSettings? EmailSettings { get; set; } - public LogSettings? LogSettings { get; set; } - public DbSettings? DbSettings { get; set; } - public ScheduleSettings? ScheduleSettings { get; set; } -} \ No newline at end of file From 202098b723a15e8baed80e3234996c0739608079 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:43:24 +0300 Subject: [PATCH 005/126] refactor: move the functionality to create a persistence database on Enpoint --- Persistence/DependencyInjection.cs | 54 ++++++++-------------------- Persistence/Properties/DbSettings.cs | 15 -------- 2 files changed, 14 insertions(+), 55 deletions(-) delete mode 100644 Persistence/Properties/DbSettings.cs diff --git a/Persistence/DependencyInjection.cs b/Persistence/DependencyInjection.cs index ed6f554..f98052c 100644 --- a/Persistence/DependencyInjection.cs +++ b/Persistence/DependencyInjection.cs @@ -1,54 +1,26 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -using Mirea.Api.DataAccess.Persistence.Properties; -using System; -using System.Collections.Generic; namespace Mirea.Api.DataAccess.Persistence; public static class DependencyInjection { - public static IServiceCollection AddPersistence(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddPersistence(this IServiceCollection services, string connection) { - var settings = configuration.GetSection(nameof(DbSettings)).Get(); - var connection = settings?.ConnectionStringSql; + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); + services.AddDbContext(DbConfig); - Dictionary> dbConfigurations = new() - { - { - DatabaseEnum.Mysql, - options => options.UseMySql(connection, ServerVersion.AutoDetect(connection)) - }, - { - DatabaseEnum.Sqlite, - options => options.UseSqlite(connection) - }, - { - DatabaseEnum.PostgresSql, - options => options.UseNpgsql(connection) - } - }; - - if (dbConfigurations.TryGetValue((DatabaseEnum)settings?.TypeDatabase!, out var dbConfig)) - { - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - services.AddDbContext(dbConfig); - - services.AddDbContext(dbConfig); - } - else - throw new NotSupportedException("Unsupported database type"); + services.AddDbContext(DbConfig); services.AddScoped(provider => provider.GetService()!); services.AddScoped(provider => provider.GetService()!); @@ -62,5 +34,7 @@ public static class DependencyInjection services.AddScoped(provider => provider.GetService()!); return services; + + void DbConfig(DbContextOptionsBuilder options) => options.UseSqlite(connection); } } \ No newline at end of file diff --git a/Persistence/Properties/DbSettings.cs b/Persistence/Properties/DbSettings.cs deleted file mode 100644 index 7ee06d9..0000000 --- a/Persistence/Properties/DbSettings.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Mirea.Api.DataAccess.Persistence.Properties; - -public enum DatabaseEnum -{ - Mysql, - Sqlite, - PostgresSql -} -public class DbSettings -{ - public bool IsDoneConfiguration { get; set; } - public DatabaseEnum TypeDatabase { get; set; } - public required string ConnectionStringSql { get; set; } - public DatabaseEnum? MigrateTo { get; set; } -} \ No newline at end of file From 99ecc4af5c7dbad51f81c9e58ce2d31972706991 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:45:31 +0300 Subject: [PATCH 006/126] fix: get connection string from configuration --- Endpoint/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 4674bc9..7c34446 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -45,7 +45,7 @@ public class Program builder.Configuration.AddJsonFile(Settings.FilePath, optional: true, reloadOnChange: true); builder.Services.AddApplication(); - builder.Services.AddPersistence(builder.Configuration); + builder.Services.AddPersistence(builder.Configuration.Get()?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); builder.Services.AddCors(options => From 817f9d2308dd8fd965747df4cfeaba7078240aed Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:47:25 +0300 Subject: [PATCH 007/126] feat: add default path --- .env | 19 +++++++++++++++++++ Backend.sln | 14 ++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..af67fc3 --- /dev/null +++ b/.env @@ -0,0 +1,19 @@ +# The .env configuration file +# Please DO NOT share this file, it contains confidential data. + +# All variables are specified according to this rule: +# DESCRIPTION - information about what the variable is responsible for +# TYPE - the type of the variable (string, boolean, etc.) +# Any additional information +# SOME_ENV_CODE=data - default data. If specified, then the variable is optional + +# General + +# The path to save the data. +# string +# (optional) +# Saving logs (if the full path is not specified), +# databases (if Sqlite) and other data that should be saved in a place other than the place where the program is launched. +# REQUIRED if the application is inside the container +# If you want to change this value, you need to change the values in Settings.json and move the file itself to the desired location. +PATH_TO_SAVE= \ No newline at end of file diff --git a/Backend.sln b/Backend.sln index 383bd42..95ebfb7 100644 --- a/Backend.sln +++ b/Backend.sln @@ -10,22 +10,20 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elements of the solution", "Elements of the solution", "{3E087889-A4A0-4A55-A07D-7D149A5BC928}" ProjectSection(SolutionItems) = preProject .dockerignore = .dockerignore + .env = .env .gitattributes = .gitattributes .gitignore = .gitignore Dockerfile = Dockerfile LICENSE.txt = LICENSE.txt README.md = README.md + .gitea\workflows\test.yaml = .gitea\workflows\test.yaml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "Application\Application.csproj", "{E7F0A4D4-B032-4BB9-9526-1AF688F341A4}" - ProjectSection(ProjectDependencies) = postProject - {C27FB5CD-6A70-4FB2-847A-847B34806902} = {C27FB5CD-6A70-4FB2-847A-847B34806902} - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "Persistence\Persistence.csproj", "{4C1E558F-633F-438E-AC3A-61CDDED917C5}" - ProjectSection(ProjectDependencies) = postProject - {E7F0A4D4-B032-4BB9-9526-1AF688F341A4} = {E7F0A4D4-B032-4BB9-9526-1AF688F341A4} - EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiDto", "ApiDto\ApiDto.csproj", "{0335FA36-E137-453F-853B-916674C168FE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -49,6 +47,10 @@ Global {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Release|Any CPU.Build.0 = Release|Any CPU + {0335FA36-E137-453F-853B-916674C168FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0335FA36-E137-453F-853B-916674C168FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0335FA36-E137-453F-853B-916674C168FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0335FA36-E137-453F-853B-916674C168FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d7299a1afdb2a8a3f25e52b46f1415f2dc56e604 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:48:23 +0300 Subject: [PATCH 008/126] feat: add wrapper for Path.Combine with default path --- Endpoint/Common/Services/PathBuilder.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Endpoint/Common/Services/PathBuilder.cs diff --git a/Endpoint/Common/Services/PathBuilder.cs b/Endpoint/Common/Services/PathBuilder.cs new file mode 100644 index 0000000..7df60d8 --- /dev/null +++ b/Endpoint/Common/Services/PathBuilder.cs @@ -0,0 +1,11 @@ +using System; +using System.IO; +using System.Linq; + +namespace Mirea.Api.Endpoint.Common.Services; + +public static class PathBuilder +{ + public static string PathToSave => Environment.GetEnvironmentVariable("PATH_TO_SAVE") ?? Directory.GetCurrentDirectory(); + public static string Combine(params string[] paths) => Path.Combine([.. paths.Prepend(PathToSave)]); +} \ No newline at end of file From fb736a1c34d6a0522bc8d8b3bb702711e5668200 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:49:40 +0300 Subject: [PATCH 009/126] feat: use path wrapper --- Endpoint/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 7c34446..bb7b69b 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; +using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Properties; using Mirea.Api.Endpoint.Configuration.Swagger; @@ -42,7 +43,8 @@ public class Program var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddConfiguration(ConfigureEnvironment()); - builder.Configuration.AddJsonFile(Settings.FilePath, optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile(PathBuilder.Combine(GeneralConfig.FilePath), optional: true, reloadOnChange: true); + builder.Services.Configure(builder.Configuration); builder.Services.AddApplication(); builder.Services.AddPersistence(builder.Configuration.Get()?.DbSettings?.ConnectionStringSql ?? string.Empty); From 7a1281692e406f54b0b8c9d6b5a5c92415df787e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:51:40 +0300 Subject: [PATCH 010/126] fix: it is correct to delete comments --- Endpoint/Configuration/EnvironmentManager.cs | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Endpoint/Configuration/EnvironmentManager.cs b/Endpoint/Configuration/EnvironmentManager.cs index cc2b201..c21d59e 100644 --- a/Endpoint/Configuration/EnvironmentManager.cs +++ b/Endpoint/Configuration/EnvironmentManager.cs @@ -5,20 +5,32 @@ namespace Mirea.Api.Endpoint.Configuration; internal static class EnvironmentManager { - public static void LoadEnvironment(string filePath) + public static void LoadEnvironment(string envFile) { - if (!File.Exists(filePath)) return; + if (!File.Exists(envFile)) return; - foreach (var line in File.ReadAllLines(filePath)) + foreach (var line in File.ReadAllLines(envFile)) { - var parts = line.Split( + if (string.IsNullOrEmpty(line)) continue; + + var commentIndex = line.IndexOf('#', StringComparison.Ordinal); + + string arg = line; + + if (commentIndex != -1) + arg = arg.Remove(commentIndex, arg.Length - commentIndex); + + var parts = arg.Split( '=', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length > 2) + parts = [parts[0], string.Join("=", parts[1..])]; + if (parts.Length != 2) continue; - Environment.SetEnvironmentVariable(parts[0].Trim(), parts[1][..(parts[1].Contains('#') ? parts[1].IndexOf('#') : parts[1].Length)].Trim()); + Environment.SetEnvironmentVariable(parts[0].Trim(), parts[1].Trim()); } } } \ No newline at end of file From 36a78a82849b1965f28db490a2d30a45b6b85941 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:53:52 +0300 Subject: [PATCH 011/126] fix: add using Configuration.General --- Endpoint/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index bb7b69b..7101c7e 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -10,7 +10,7 @@ using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration; -using Mirea.Api.Endpoint.Properties; +using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; using System; From 0e9bb04b96025aafe9ed32e1de46cae0c902eae8 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:56:25 +0300 Subject: [PATCH 012/126] feat: add setup token --- Endpoint/Common/Interfaces/ISetupToken.cs | 9 ++++++ .../General/SetupTokenService.cs | 28 +++++++++++++++++++ Endpoint/Program.cs | 2 ++ 3 files changed, 39 insertions(+) create mode 100644 Endpoint/Common/Interfaces/ISetupToken.cs create mode 100644 Endpoint/Configuration/General/SetupTokenService.cs diff --git a/Endpoint/Common/Interfaces/ISetupToken.cs b/Endpoint/Common/Interfaces/ISetupToken.cs new file mode 100644 index 0000000..462b759 --- /dev/null +++ b/Endpoint/Common/Interfaces/ISetupToken.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mirea.Api.Endpoint.Common.Interfaces; + +public interface ISetupToken +{ + bool MatchToken(ReadOnlySpan token); + void SetToken(ReadOnlySpan token); +} \ No newline at end of file diff --git a/Endpoint/Configuration/General/SetupTokenService.cs b/Endpoint/Configuration/General/SetupTokenService.cs new file mode 100644 index 0000000..4cc1216 --- /dev/null +++ b/Endpoint/Configuration/General/SetupTokenService.cs @@ -0,0 +1,28 @@ +using Mirea.Api.Endpoint.Common.Interfaces; +using System; + +namespace Mirea.Api.Endpoint.Configuration.General; + +public class SetupTokenService : ISetupToken +{ + public ReadOnlyMemory? Token { get; private set; } + + public bool MatchToken(ReadOnlySpan token) + { + if (Token == null || token.Length != Token.Value.Length) + return false; + + var token2 = Token.Value.Span; + + int result = 0; + for (int i = 0; i < Token.Value.Length; i++) + result |= token2[i] ^ token[i]; + + return result == 0; + } + + public void SetToken(ReadOnlySpan token) + { + Token = token.ToArray(); + } +} \ No newline at end of file diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 7101c7e..4d79328 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; +using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; @@ -50,6 +51,7 @@ public class Program builder.Services.AddPersistence(builder.Configuration.Get()?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); + builder.Services.AddSingleton(); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => From baedc667b7faacb6d5b9bf1df7235a5d85b143ed Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:57:54 +0300 Subject: [PATCH 013/126] feat: add PairPeriod for ApiDto --- ApiDto/Common/PairPeriodTime.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 ApiDto/Common/PairPeriodTime.cs diff --git a/ApiDto/Common/PairPeriodTime.cs b/ApiDto/Common/PairPeriodTime.cs new file mode 100644 index 0000000..89b0732 --- /dev/null +++ b/ApiDto/Common/PairPeriodTime.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Common; + +/// +/// Represents a pair of time periods. +/// +public class PairPeriodTime +{ + /// + /// Gets or sets the start time of the period. + /// + [Required] + public TimeOnly Start { get; set; } + + /// + /// Gets or sets the end time of the period. + /// + [Required] + public TimeOnly End { get; set; } +} \ No newline at end of file From 7b779463bbd2711c82f374f5b90e14e1ea077d39 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 06:59:28 +0300 Subject: [PATCH 014/126] feat: add converter from Dto.PairPeriodTime toEnpoint.PairPeriodTime and vice versa --- Endpoint/Common/Services/PairPeriodTimeConverter.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Endpoint/Common/Services/PairPeriodTimeConverter.cs diff --git a/Endpoint/Common/Services/PairPeriodTimeConverter.cs b/Endpoint/Common/Services/PairPeriodTimeConverter.cs new file mode 100644 index 0000000..ad46295 --- /dev/null +++ b/Endpoint/Common/Services/PairPeriodTimeConverter.cs @@ -0,0 +1,13 @@ +using Mirea.Api.Endpoint.Configuration.General.Settings; +using System.Collections.Generic; +using System.Linq; + +namespace Mirea.Api.Endpoint.Common.Services; + +public static class PairPeriodTimeConverter +{ + public static Dictionary ConvertToDto(this IDictionary pairPeriod) => + pairPeriod.ToDictionary(kvp => kvp.Key, kvp => new Dto.Common.PairPeriodTime { Start = kvp.Value.Start, End = kvp.Value.End }); + + public static Dictionary ConvertFromDto(this IDictionary pairPeriod) => pairPeriod.ToDictionary(kvp => kvp.Key, kvp => new ScheduleSettings.PairPeriodTime(kvp.Value.Start, kvp.Value.End)); +} From d1a806545d9f5e746a204a82aadca7b33db76149 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:01:23 +0300 Subject: [PATCH 015/126] feat: add maintenance mode --- .../IMaintenanceModeNotConfigureService.cs | 8 ++++++++ .../Common/Interfaces/IMaintenanceModeService.cs | 10 ++++++++++ .../Services/MaintenanceModeNotConfigureService.cs | 11 +++++++++++ Endpoint/Common/Services/MaintenanceModeService.cs | 14 ++++++++++++++ Endpoint/Program.cs | 2 ++ 5 files changed, 45 insertions(+) create mode 100644 Endpoint/Common/Interfaces/IMaintenanceModeNotConfigureService.cs create mode 100644 Endpoint/Common/Interfaces/IMaintenanceModeService.cs create mode 100644 Endpoint/Common/Services/MaintenanceModeNotConfigureService.cs create mode 100644 Endpoint/Common/Services/MaintenanceModeService.cs diff --git a/Endpoint/Common/Interfaces/IMaintenanceModeNotConfigureService.cs b/Endpoint/Common/Interfaces/IMaintenanceModeNotConfigureService.cs new file mode 100644 index 0000000..e2e6a5d --- /dev/null +++ b/Endpoint/Common/Interfaces/IMaintenanceModeNotConfigureService.cs @@ -0,0 +1,8 @@ +namespace Mirea.Api.Endpoint.Common.Interfaces; + +public interface IMaintenanceModeNotConfigureService +{ + bool IsMaintenanceMode { get; } + + void DisableMaintenanceMode(); +} \ No newline at end of file diff --git a/Endpoint/Common/Interfaces/IMaintenanceModeService.cs b/Endpoint/Common/Interfaces/IMaintenanceModeService.cs new file mode 100644 index 0000000..7f2d7cb --- /dev/null +++ b/Endpoint/Common/Interfaces/IMaintenanceModeService.cs @@ -0,0 +1,10 @@ +namespace Mirea.Api.Endpoint.Common.Interfaces; + +public interface IMaintenanceModeService +{ + bool IsMaintenanceMode { get; } + + void EnableMaintenanceMode(); + + void DisableMaintenanceMode(); +} \ No newline at end of file diff --git a/Endpoint/Common/Services/MaintenanceModeNotConfigureService.cs b/Endpoint/Common/Services/MaintenanceModeNotConfigureService.cs new file mode 100644 index 0000000..b11516b --- /dev/null +++ b/Endpoint/Common/Services/MaintenanceModeNotConfigureService.cs @@ -0,0 +1,11 @@ +using Mirea.Api.Endpoint.Common.Interfaces; + +namespace Mirea.Api.Endpoint.Common.Services; + +public class MaintenanceModeNotConfigureService : IMaintenanceModeNotConfigureService +{ + public bool IsMaintenanceMode { get; private set; } = true; + + public void DisableMaintenanceMode() => + IsMaintenanceMode = false; +} \ No newline at end of file diff --git a/Endpoint/Common/Services/MaintenanceModeService.cs b/Endpoint/Common/Services/MaintenanceModeService.cs new file mode 100644 index 0000000..a967fd1 --- /dev/null +++ b/Endpoint/Common/Services/MaintenanceModeService.cs @@ -0,0 +1,14 @@ +using Mirea.Api.Endpoint.Common.Interfaces; + +namespace Mirea.Api.Endpoint.Common.Services; + +public class MaintenanceModeService : IMaintenanceModeService +{ + public bool IsMaintenanceMode { get; private set; }; + + public void EnableMaintenanceMode() => + IsMaintenanceMode = true; + + public void DisableMaintenanceMode() => + IsMaintenanceMode = false; +} \ No newline at end of file diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 4d79328..a238bbf 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -51,6 +51,8 @@ public class Program builder.Services.AddPersistence(builder.Configuration.Get()?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddCors(options => { From b62ddc9015ae2a0fe72413b93c3649bfab41f300 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:01:58 +0300 Subject: [PATCH 016/126] fix: delete ';' from property --- Endpoint/Common/Services/MaintenanceModeService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endpoint/Common/Services/MaintenanceModeService.cs b/Endpoint/Common/Services/MaintenanceModeService.cs index a967fd1..ef57365 100644 --- a/Endpoint/Common/Services/MaintenanceModeService.cs +++ b/Endpoint/Common/Services/MaintenanceModeService.cs @@ -4,7 +4,7 @@ namespace Mirea.Api.Endpoint.Common.Services; public class MaintenanceModeService : IMaintenanceModeService { - public bool IsMaintenanceMode { get; private set; }; + public bool IsMaintenanceMode { get; private set; } public void EnableMaintenanceMode() => IsMaintenanceMode = true; From af284e945f6b4e8e8c2b3da07d8407ebbb632ae9 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:02:35 +0300 Subject: [PATCH 017/126] feat: add attribute maintenance ignore for controllers --- .../Common/Attributes/MaintenanceModeIgnoreAttribute.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Endpoint/Common/Attributes/MaintenanceModeIgnoreAttribute.cs diff --git a/Endpoint/Common/Attributes/MaintenanceModeIgnoreAttribute.cs b/Endpoint/Common/Attributes/MaintenanceModeIgnoreAttribute.cs new file mode 100644 index 0000000..6f69b7a --- /dev/null +++ b/Endpoint/Common/Attributes/MaintenanceModeIgnoreAttribute.cs @@ -0,0 +1,6 @@ +using System; + +namespace Mirea.Api.Endpoint.Common.Attributes; + +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public class MaintenanceModeIgnoreAttribute : Attribute; \ No newline at end of file From 3f30b98cf948ea1dc5f8904e328085d046148c8c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:04:07 +0300 Subject: [PATCH 018/126] feat: add middleware for ignore maintenance --- .../Middleware/MaintenanceModeMiddleware.cs | 39 +++++++++++++++++++ Endpoint/Program.cs | 2 + 2 files changed, 41 insertions(+) create mode 100644 Endpoint/Middleware/MaintenanceModeMiddleware.cs diff --git a/Endpoint/Middleware/MaintenanceModeMiddleware.cs b/Endpoint/Middleware/MaintenanceModeMiddleware.cs new file mode 100644 index 0000000..2bd5d3b --- /dev/null +++ b/Endpoint/Middleware/MaintenanceModeMiddleware.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Http; +using Mirea.Api.Endpoint.Common.Attributes; +using Mirea.Api.Endpoint.Common.Interfaces; +using System.Threading.Tasks; + +namespace Mirea.Api.Endpoint.Middleware; + +public class MaintenanceModeMiddleware(RequestDelegate next, IMaintenanceModeService maintenanceModeService, IMaintenanceModeNotConfigureService maintenanceModeNotConfigureService) +{ + private static bool IsIgnoreMaintenanceMode(HttpContext context) + { + var endpoint = context.GetEndpoint(); + return endpoint?.Metadata.GetMetadata() != null; + } + + public async Task Invoke(HttpContext context) + { + if (!maintenanceModeService.IsMaintenanceMode && !maintenanceModeNotConfigureService.IsMaintenanceMode || IsIgnoreMaintenanceMode(context)) + await next(context); + else + { + + context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; + context.Response.ContentType = "plain/text"; + + string error; + if (maintenanceModeService.IsMaintenanceMode) + { + context.Response.Headers.RetryAfter = "600"; + error = "The service is currently undergoing maintenance. Please try again later."; + } + else + error = + "The service is currently not configured. Go to the setup page if you are an administrator or try again later."; + + await context.Response.WriteAsync(error); + } + } +} \ No newline at end of file diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index a238bbf..340478c 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -13,6 +13,7 @@ using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.Swagger; +using Mirea.Api.Endpoint.Middleware; using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections; @@ -118,6 +119,7 @@ public class Program } }); } + app.UseMiddleware(); app.UseHttpsRedirection(); From ae0b9daefa9c5ccad79446514f6e7fa0e96e2eac Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:09:40 +0300 Subject: [PATCH 019/126] refactor: change the api path --- Endpoint/Controllers/BaseController.cs | 3 +- Endpoint/Controllers/V1/BaseControllerV1.cs | 8 - Endpoint/Controllers/V1/CampusController.cs | 90 ++++----- .../Controllers/V1/DisciplineController.cs | 96 ++++----- Endpoint/Controllers/V1/FacultyController.cs | 104 +++++----- Endpoint/Controllers/V1/GroupController.cs | 182 +++++++++--------- .../Controllers/V1/LectureHallController.cs | 136 ++++++------- .../Controllers/V1/ProfessorController.cs | 3 +- Endpoint/Controllers/V1/ScheduleController.cs | 12 +- 9 files changed, 314 insertions(+), 320 deletions(-) delete mode 100644 Endpoint/Controllers/V1/BaseControllerV1.cs diff --git a/Endpoint/Controllers/BaseController.cs b/Endpoint/Controllers/BaseController.cs index 82c4c7a..53f02a1 100644 --- a/Endpoint/Controllers/BaseController.cs +++ b/Endpoint/Controllers/BaseController.cs @@ -2,6 +2,7 @@ namespace Mirea.Api.Endpoint.Controllers; +[Produces("application/json")] +[Route("api/v{version:apiVersion}/[controller]")] [ApiController] -[Route("api/[controller]/[action]")] public class BaseController : ControllerBase; \ No newline at end of file diff --git a/Endpoint/Controllers/V1/BaseControllerV1.cs b/Endpoint/Controllers/V1/BaseControllerV1.cs deleted file mode 100644 index 6ebe1f3..0000000 --- a/Endpoint/Controllers/V1/BaseControllerV1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Mirea.Api.Endpoint.Controllers.V1; - -[ApiVersion("1.0")] -[Produces("application/json")] -[Route("api/v{version:apiVersion}/[controller]/[action]")] -public class BaseControllerV1 : BaseController; \ No newline at end of file diff --git a/Endpoint/Controllers/V1/CampusController.cs b/Endpoint/Controllers/V1/CampusController.cs index ae83834..4f4f101 100644 --- a/Endpoint/Controllers/V1/CampusController.cs +++ b/Endpoint/Controllers/V1/CampusController.cs @@ -9,53 +9,53 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Mirea.Api.Endpoint.Controllers.V1 +namespace Mirea.Api.Endpoint.Controllers.V1; + +[ApiVersion("1.0")] +public class CampusController(IMediator mediator) : BaseController { - public class CampusController(IMediator mediator) : BaseControllerV1 + /// + /// Gets basic information about campuses. + /// + /// Basic information about campuses. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task>> Get() { - /// - /// Gets basic information about campuses. - /// - /// Basic information about campuses. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> Get() - { - var result = await mediator.Send(new GetCampusBasicInfoListQuery()); + var result = await mediator.Send(new GetCampusBasicInfoListQuery()); - return Ok(result.Campuses - .Select(c => new CampusBasicInfoResponse() - { - Id = c.Id, - CodeName = c.CodeName, - FullName = c.FullName - }) - ); - } - - /// - /// Gets details of a specific campus by ID. - /// - /// Campus ID. - /// Details of the specified campus. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task> GetDetails(int id) - { - var result = await mediator.Send(new GetCampusDetailsQuery() + return Ok(result.Campuses + .Select(c => new CampusBasicInfoResponse() { - Id = id - }); - - return Ok(new CampusDetailsResponse() - { - Id = result.Id, - CodeName = result.CodeName, - FullName = result.FullName, - Address = result.Address - }); - } + Id = c.Id, + CodeName = c.CodeName, + FullName = c.FullName + }) + ); } -} + + /// + /// Gets details of a specific campus by ID. + /// + /// Campus ID. + /// Details of the specified campus. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task> GetDetails(int id) + { + var result = await mediator.Send(new GetCampusDetailsQuery() + { + Id = id + }); + + return Ok(new CampusDetailsResponse() + { + Id = result.Id, + CodeName = result.CodeName, + FullName = result.FullName, + Address = result.Address + }); + } +} \ No newline at end of file diff --git a/Endpoint/Controllers/V1/DisciplineController.cs b/Endpoint/Controllers/V1/DisciplineController.cs index 056b208..1de1136 100644 --- a/Endpoint/Controllers/V1/DisciplineController.cs +++ b/Endpoint/Controllers/V1/DisciplineController.cs @@ -9,57 +9,57 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Mirea.Api.Endpoint.Controllers.V1 +namespace Mirea.Api.Endpoint.Controllers.V1; + +[ApiVersion("1.0")] +public class DisciplineController(IMediator mediator) : BaseController { - public class DisciplineController(IMediator mediator) : BaseControllerV1 + /// + /// Gets a paginated list of disciplines. + /// + /// Page number. Start from 0. + /// Number of items per page. + /// Paginated list of disciplines. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) { - /// - /// Gets a paginated list of disciplines. - /// - /// Page number. Start from 0. - /// Number of items per page. - /// Paginated list of disciplines. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) + var result = await mediator.Send(new GetDisciplineListQuery() { - var result = await mediator.Send(new GetDisciplineListQuery() - { - Page = page, - PageSize = pageSize - }); + Page = page, + PageSize = pageSize + }); - return Ok(result.Disciplines - .Select(d => new DisciplineResponse() - { - Id = d.Id, - Name = d.Name - }) - ); - } - - /// - /// Gets details of a specific discipline by ID. - /// - /// Discipline ID. - /// Details of the specified discipline. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task> GetDetails(int id) - { - var result = await mediator.Send(new GetDisciplineInfoQuery() + return Ok(result.Disciplines + .Select(d => new DisciplineResponse() { - Id = id - }); - - return Ok(new DisciplineResponse() - { - Id = result.Id, - Name = result.Name - }); - } + Id = d.Id, + Name = d.Name + }) + ); } -} + + /// + /// Gets details of a specific discipline by ID. + /// + /// Discipline ID. + /// Details of the specified discipline. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task> GetDetails(int id) + { + var result = await mediator.Send(new GetDisciplineInfoQuery() + { + Id = id + }); + + return Ok(new DisciplineResponse() + { + Id = result.Id, + Name = result.Name + }); + } +} \ No newline at end of file diff --git a/Endpoint/Controllers/V1/FacultyController.cs b/Endpoint/Controllers/V1/FacultyController.cs index 3b4cd61..9652551 100644 --- a/Endpoint/Controllers/V1/FacultyController.cs +++ b/Endpoint/Controllers/V1/FacultyController.cs @@ -9,61 +9,61 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Mirea.Api.Endpoint.Controllers.V1 +namespace Mirea.Api.Endpoint.Controllers.V1; + +[ApiVersion("1.0")] +public class FacultyController(IMediator mediator) : BaseController { - public class FacultyController(IMediator mediator) : BaseControllerV1 + /// + /// Gets a paginated list of faculties. + /// + /// Page number. Start from 0. + /// Number of items per page. + /// Paginated list of faculties. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) { - /// - /// Gets a paginated list of faculties. - /// - /// Page number. Start from 0. - /// Number of items per page. - /// Paginated list of faculties. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) + var result = await mediator.Send(new GetFacultyListQuery() { - var result = await mediator.Send(new GetFacultyListQuery() - { - Page = page, - PageSize = pageSize - }); + Page = page, + PageSize = pageSize + }); - return Ok(result.Faculties - .Select(f => new FacultyResponse() - { - Id = f.Id, - Name = f.Name, - CampusId = f.CampusId - }) - ); - } - - /// - /// Gets details of a specific faculty by ID. - /// - /// Faculty ID. - /// Details of the specified faculty. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task> GetDetails(int id) - { - var result = await mediator.Send(new GetFacultyInfoQuery() + return Ok(result.Faculties + .Select(f => new FacultyResponse() { - Id = id - }); - - return Ok(new FacultyDetailsResponse() - { - Id = result.Id, - Name = result.Name, - CampusId = result.CampusId, - CampusCode = result.CampusCode, - CampusName = result.CampusName - }); - } + Id = f.Id, + Name = f.Name, + CampusId = f.CampusId + }) + ); } -} + + /// + /// Gets details of a specific faculty by ID. + /// + /// Faculty ID. + /// Details of the specified faculty. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task> GetDetails(int id) + { + var result = await mediator.Send(new GetFacultyInfoQuery() + { + Id = id + }); + + return Ok(new FacultyDetailsResponse() + { + Id = result.Id, + Name = result.Name, + CampusId = result.CampusId, + CampusCode = result.CampusCode, + CampusName = result.CampusName + }); + } +} \ No newline at end of file diff --git a/Endpoint/Controllers/V1/GroupController.cs b/Endpoint/Controllers/V1/GroupController.cs index cead029..c12f3da 100644 --- a/Endpoint/Controllers/V1/GroupController.cs +++ b/Endpoint/Controllers/V1/GroupController.cs @@ -10,99 +10,99 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Mirea.Api.Endpoint.Controllers.V1 +namespace Mirea.Api.Endpoint.Controllers.V1; + +[ApiVersion("1.0")] +public class GroupController(IMediator mediator) : BaseController { - public class GroupController(IMediator mediator) : BaseControllerV1 + private static int GetCourseNumber(string groupName) { - private static int GetCourseNumber(string groupName) - { - var current = DateTime.Now; - if (!int.TryParse(groupName[2..], out var yearOfGroup) - && !int.TryParse(groupName.Split('-')[^1][..2], out yearOfGroup)) - return -1; + var current = DateTime.Now; + if (!int.TryParse(groupName[2..], out var yearOfGroup) + && !int.TryParse(groupName.Split('-')[^1][..2], out yearOfGroup)) + return -1; - // Convert a two-digit year to a four-digit one - yearOfGroup += current.Year / 100 * 100; + // Convert a two-digit year to a four-digit one + yearOfGroup += current.Year / 100 * 100; - return current.Year - yearOfGroup + (current.Month < 9 ? 0 : 1); - } - - /// - /// Retrieves a list of groups. - /// - /// The page number for pagination (optional). - /// The page size for pagination (optional). - /// A list of groups. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) - { - var result = await mediator.Send(new GetGroupListQuery() - { - Page = page, - PageSize = pageSize - }); - - return Ok(result.Groups - .Select(g => new GroupResponse() - { - Id = g.Id, - Name = g.Name, - FacultyId = g.FacultyId, - CourseNumber = GetCourseNumber(g.Name) - }) - ); - } - - /// - /// Retrieves detailed information about a specific group. - /// - /// The ID of the group to retrieve. - /// Detailed information about the group. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task> GetDetails(int id) - { - var result = await mediator.Send(new GetGroupInfoQuery() - { - Id = id - }); - - return Ok(new GroupDetailsResponse() - { - Id = result.Id, - Name = result.Name, - FacultyId = result.FacultyId, - FacultyName = result.Faculty, - CourseNumber = GetCourseNumber(result.Name) - }); - } - - /// - /// Retrieves a list of groups by faculty ID. - /// - /// The ID of the faculty. - /// A list of groups belonging to the specified faculty. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task>> GetByFaculty(int id) - { - var result = await mediator.Send(new GetGroupListQuery()); - - return Ok(result.Groups - .Where(g => g.FacultyId == id) - .Select(g => new GroupResponse() - { - Id = g.Id, - Name = g.Name, - CourseNumber = GetCourseNumber(g.Name), - FacultyId = g.FacultyId - })); - } + return current.Year - yearOfGroup + (current.Month < 9 ? 0 : 1); } -} + + /// + /// Retrieves a list of groups. + /// + /// The page number for pagination (optional). + /// The page size for pagination (optional). + /// A list of groups. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + public async Task>> Get([FromQuery] int? page, [FromQuery] int? pageSize) + { + var result = await mediator.Send(new GetGroupListQuery() + { + Page = page, + PageSize = pageSize + }); + + return Ok(result.Groups + .Select(g => new GroupResponse() + { + Id = g.Id, + Name = g.Name, + FacultyId = g.FacultyId, + CourseNumber = GetCourseNumber(g.Name) + }) + ); + } + + /// + /// Retrieves detailed information about a specific group. + /// + /// The ID of the group to retrieve. + /// Detailed information about the group. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task> GetDetails(int id) + { + var result = await mediator.Send(new GetGroupInfoQuery() + { + Id = id + }); + + return Ok(new GroupDetailsResponse() + { + Id = result.Id, + Name = result.Name, + FacultyId = result.FacultyId, + FacultyName = result.Faculty, + CourseNumber = GetCourseNumber(result.Name) + }); + } + + /// + /// Retrieves a list of groups by faculty ID. + /// + /// The ID of the faculty. + /// A list of groups belonging to the specified faculty. + [HttpGet("GetByFaculty/{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task>> GetByFaculty(int id) + { + var result = await mediator.Send(new GetGroupListQuery()); + + return Ok(result.Groups + .Where(g => g.FacultyId == id) + .Select(g => new GroupResponse() + { + Id = g.Id, + Name = g.Name, + CourseNumber = GetCourseNumber(g.Name), + FacultyId = g.FacultyId + })); + } +} \ No newline at end of file diff --git a/Endpoint/Controllers/V1/LectureHallController.cs b/Endpoint/Controllers/V1/LectureHallController.cs index de2f21f..c6e67b8 100644 --- a/Endpoint/Controllers/V1/LectureHallController.cs +++ b/Endpoint/Controllers/V1/LectureHallController.cs @@ -9,76 +9,76 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Mirea.Api.Endpoint.Controllers.V1 +namespace Mirea.Api.Endpoint.Controllers.V1; + +[ApiVersion("1.0")] +public class LectureHallController(IMediator mediator) : BaseController { - public class LectureHallController(IMediator mediator) : BaseControllerV1 + /// + /// Retrieves a list of all lecture halls. + /// + /// A list of lecture halls. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task>> Get() { - /// - /// Retrieves a list of all lecture halls. - /// - /// A list of lecture halls. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> Get() - { - var result = await mediator.Send(new GetLectureHallListQuery()); + var result = await mediator.Send(new GetLectureHallListQuery()); - return Ok(result.LectureHalls - .Select(l => new LectureHallResponse() - { - Id = l.Id, - Name = l.Name, - CampusId = l.CampusId - }) - ); - } - - /// - /// Retrieves details of a specific lecture hall by its ID. - /// - /// The ID of the lecture hall to retrieve. - /// The details of the specified lecture hall. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task> GetDetails(int id) - { - var result = await mediator.Send(new GetLectureHallInfoQuery() + return Ok(result.LectureHalls + .Select(l => new LectureHallResponse() { - Id = id - }); - - return Ok(new LectureHallDetailsResponse() - { - Id = result.Id, - Name = result.Name, - CampusId = result.CampusId, - CampusCode = result.CampusCode, - CampusName = result.CampusName - }); - } - - /// - /// Retrieves a list of lecture halls by campus ID. - /// - /// The ID of the campus. - /// A list of lecture halls in the specified campus. - [HttpGet("{id:int}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [BadRequestResponse] - [NotFoundResponse] - public async Task>> GetByCampus(int id) - { - var result = await mediator.Send(new GetLectureHallListQuery()); - - return Ok(result.LectureHalls.Where(l => l.CampusId == id) - .Select(l => new LectureHallResponse() - { - Id = l.Id, - Name = l.Name, - CampusId = l.CampusId - })); - } + Id = l.Id, + Name = l.Name, + CampusId = l.CampusId + }) + ); } -} + + /// + /// Retrieves details of a specific lecture hall by its ID. + /// + /// The ID of the lecture hall to retrieve. + /// The details of the specified lecture hall. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task> GetDetails(int id) + { + var result = await mediator.Send(new GetLectureHallInfoQuery() + { + Id = id + }); + + return Ok(new LectureHallDetailsResponse() + { + Id = result.Id, + Name = result.Name, + CampusId = result.CampusId, + CampusCode = result.CampusCode, + CampusName = result.CampusName + }); + } + + /// + /// Retrieves a list of lecture halls by campus ID. + /// + /// The ID of the campus. + /// A list of lecture halls in the specified campus. + [HttpGet("GetByCampus/{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [BadRequestResponse] + [NotFoundResponse] + public async Task>> GetByCampus(int id) + { + var result = await mediator.Send(new GetLectureHallListQuery()); + + return Ok(result.LectureHalls.Where(l => l.CampusId == id) + .Select(l => new LectureHallResponse() + { + Id = l.Id, + Name = l.Name, + CampusId = l.CampusId + })); + } +} \ No newline at end of file diff --git a/Endpoint/Controllers/V1/ProfessorController.cs b/Endpoint/Controllers/V1/ProfessorController.cs index 5a9053a..3093a38 100644 --- a/Endpoint/Controllers/V1/ProfessorController.cs +++ b/Endpoint/Controllers/V1/ProfessorController.cs @@ -11,7 +11,8 @@ using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Controllers.V1; -public class ProfessorController(IMediator mediator) : BaseControllerV1 +[ApiVersion("1.0")] +public class ProfessorController(IMediator mediator) : BaseController { /// /// Retrieves a list of professors. diff --git a/Endpoint/Controllers/V1/ScheduleController.cs b/Endpoint/Controllers/V1/ScheduleController.cs index 1500efa..a0d5b7b 100644 --- a/Endpoint/Controllers/V1/ScheduleController.cs +++ b/Endpoint/Controllers/V1/ScheduleController.cs @@ -12,7 +12,8 @@ using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Controllers.V1; -public class ScheduleController(IMediator mediator) : BaseControllerV1 +[ApiVersion("1.0")] +public class ScheduleController(IMediator mediator) : BaseController { /// /// Retrieves schedules based on various filters. @@ -25,7 +26,6 @@ public class ScheduleController(IMediator mediator) : BaseControllerV1 [BadRequestResponse] [NotFoundResponse] public async Task>> Get([FromBody] ScheduleRequest request) - { if ((request.Groups == null || request.Groups.Length == 0) && (request.Disciplines == null || request.Disciplines.Length == 0) && @@ -85,7 +85,7 @@ public class ScheduleController(IMediator mediator) : BaseControllerV1 /// An array of professor IDs. /// An array of lecture hall IDs. /// A response containing schedules for the specified group. - [HttpGet("{id:int}")] + [HttpGet("GetByGroup/{id:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [BadRequestResponse] @@ -142,7 +142,7 @@ public class ScheduleController(IMediator mediator) : BaseControllerV1 /// An array of group IDs. /// An array of lecture hall IDs. /// A response containing schedules for the specified professor. - [HttpGet("{id:int}")] + [HttpGet("GetByProfessor/{id:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [BadRequestResponse] @@ -203,7 +203,7 @@ public class ScheduleController(IMediator mediator) : BaseControllerV1 /// An array of professor IDs. /// An array of group IDs. /// A response containing schedules for the specified lecture hall. - [HttpGet("{id:int}")] + [HttpGet("GetByLectureHall/{id:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [BadRequestResponse] @@ -264,7 +264,7 @@ public class ScheduleController(IMediator mediator) : BaseControllerV1 /// An array of professor IDs. /// An array of lecture hall IDs. /// A response containing schedules for the specified discipline. - [HttpGet("{id:int}")] + [HttpGet("GetByDiscipline/{id:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [BadRequestResponse] From 08f13108d8ddff52e65003b54d49540fd0bd630d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:10:32 +0300 Subject: [PATCH 020/126] feat: add validator for settings --- .../Validators/SettingsRequiredValidator.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Endpoint/Configuration/General/Validators/SettingsRequiredValidator.cs diff --git a/Endpoint/Configuration/General/Validators/SettingsRequiredValidator.cs b/Endpoint/Configuration/General/Validators/SettingsRequiredValidator.cs new file mode 100644 index 0000000..a1e8fbc --- /dev/null +++ b/Endpoint/Configuration/General/Validators/SettingsRequiredValidator.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Options; +using Mirea.Api.Endpoint.Configuration.General.Attributes; +using Mirea.Api.Endpoint.Configuration.General.Interfaces; +using System; +using System.Reflection; + +namespace Mirea.Api.Endpoint.Configuration.General.Validators; + +public class SettingsRequiredValidator +{ + private readonly GeneralConfig _generalConfig; + + public SettingsRequiredValidator(IOptionsSnapshot configuration) => + _generalConfig = configuration.Value; + + public SettingsRequiredValidator(GeneralConfig configuration) => + _generalConfig = configuration; + + public bool AreSettingsValid() + { + foreach (var property in _generalConfig + .GetType() + .GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (!Attribute.IsDefined(property.PropertyType, typeof(RequiredSettingsAttribute))) continue; + + var value = property.GetValue(_generalConfig); + if (value == null) + return false; + + var isConfigured = value as IIsConfigured; + if (!isConfigured!.IsConfigured()) + return false; + } + + return true; + } +} \ No newline at end of file From 35eb1eab39791838ca3fa75ceb2f622ea8bde06c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:12:58 +0300 Subject: [PATCH 021/126] feat: implement validation of settings --- Endpoint/Program.cs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 340478c..0938434 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -12,6 +12,7 @@ using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; +using Mirea.Api.Endpoint.Configuration.General.Validators; using Mirea.Api.Endpoint.Configuration.Swagger; using Mirea.Api.Endpoint.Middleware; using Swashbuckle.AspNetCore.SwaggerGen; @@ -100,8 +101,25 @@ public class Program Console.WriteLine($"{item.Key}:{item.Value}"); #endif - var uber = app.Services.CreateScope().ServiceProvider.GetService(); - DbInitializer.Initialize(uber!); + using (var scope = app.Services.CreateScope()) + { + var serviceProvider = scope.ServiceProvider; + + var optionsSnapshot = serviceProvider.GetRequiredService>(); + var settingsValidator = new SettingsRequiredValidator(optionsSnapshot); + var isDoneConfig = settingsValidator.AreSettingsValid(); + + if (isDoneConfig) + { + var uberDbContext = serviceProvider.GetRequiredService(); + var maintenanceModeService = serviceProvider.GetRequiredService(); + + maintenanceModeService.DisableMaintenanceMode(); + DbInitializer.Initialize(uberDbContext); + + // todo: if admin not found + } + } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) From fb6e119a34643db91ef78d8abeaecfa94c3da4eb Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:14:17 +0300 Subject: [PATCH 022/126] feat: add token for setup controllers --- .../TokenAuthenticationAttribute.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Endpoint/Common/Attributes/TokenAuthenticationAttribute.cs diff --git a/Endpoint/Common/Attributes/TokenAuthenticationAttribute.cs b/Endpoint/Common/Attributes/TokenAuthenticationAttribute.cs new file mode 100644 index 0000000..81812bf --- /dev/null +++ b/Endpoint/Common/Attributes/TokenAuthenticationAttribute.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Mirea.Api.Endpoint.Common.Interfaces; +using System; + +namespace Mirea.Api.Endpoint.Common.Attributes; + +[AttributeUsage(AttributeTargets.Method)] +public class TokenAuthenticationAttribute : Attribute, IActionFilter +{ + public void OnActionExecuting(ActionExecutingContext context) + { + var setupToken = context.HttpContext.RequestServices.GetRequiredService(); + if (!context.HttpContext.Request.Cookies.TryGetValue("AuthToken", out string? tokenFromCookie)) + { + context.Result = new UnauthorizedResult(); + return; + } + + if (setupToken.MatchToken(Convert.FromBase64String(tokenFromCookie))) return; + + context.Result = new UnauthorizedResult(); + } + + public void OnActionExecuted(ActionExecutedContext context) { } +} \ No newline at end of file From 59785f600fc3b0afe81c9a6677b56be530789722 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:15:13 +0300 Subject: [PATCH 023/126] feat: add argument exception for controllers --- Endpoint/Common/Exceptions/ControllerArgumentException.cs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Endpoint/Common/Exceptions/ControllerArgumentException.cs diff --git a/Endpoint/Common/Exceptions/ControllerArgumentException.cs b/Endpoint/Common/Exceptions/ControllerArgumentException.cs new file mode 100644 index 0000000..36e6b26 --- /dev/null +++ b/Endpoint/Common/Exceptions/ControllerArgumentException.cs @@ -0,0 +1,5 @@ +using System; + +namespace Mirea.Api.Endpoint.Common.Exceptions; + +public class ControllerArgumentException(string message) : Exception(message); \ No newline at end of file From 481839159cdb60c7bf8f02f92d27a91878c3a30c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:16:15 +0300 Subject: [PATCH 024/126] feat: add middleware for custom exception --- .../CustomExceptionHandlerMiddleware.cs | 60 +++++++++++++++++++ Endpoint/Program.cs | 1 + 2 files changed, 61 insertions(+) create mode 100644 Endpoint/Middleware/CustomExceptionHandlerMiddleware.cs diff --git a/Endpoint/Middleware/CustomExceptionHandlerMiddleware.cs b/Endpoint/Middleware/CustomExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..62c41e2 --- /dev/null +++ b/Endpoint/Middleware/CustomExceptionHandlerMiddleware.cs @@ -0,0 +1,60 @@ +using FluentValidation; +using Microsoft.AspNetCore.Http; +using Mirea.Api.DataAccess.Application.Common.Exceptions; +using Mirea.Api.Dto.Responses; +using Mirea.Api.Endpoint.Common.Exceptions; +using System; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Mirea.Api.Endpoint.Middleware; + +public class CustomExceptionHandlerMiddleware(RequestDelegate next) +{ + public async Task Invoke(HttpContext context) + { + try + { + await next(context); + } + catch (Exception exception) + { + await HandleExceptionAsync(context, exception); + } + } + + private static Task HandleExceptionAsync(HttpContext context, Exception exception) + { + var code = StatusCodes.Status500InternalServerError; + var result = string.Empty; + switch (exception) + { + case ValidationException validationException: + code = StatusCodes.Status400BadRequest; + result = JsonSerializer.Serialize(new ErrorResponse() + { + Error = validationException.Message, + Code = code + }); + break; + case NotFoundException: + code = StatusCodes.Status404NotFound; + break; + case ControllerArgumentException: + code = StatusCodes.Status400BadRequest; + break; + } + + context.Response.ContentType = "application/json"; + context.Response.StatusCode = code; + + if (string.IsNullOrEmpty(result)) + result = JsonSerializer.Serialize(new ErrorResponse() + { + Error = exception.Message, + Code = code + }); + + return context.Response.WriteAsync(result); + } +} \ No newline at end of file diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 0938434..89d98ab 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -138,6 +138,7 @@ public class Program }); } app.UseMiddleware(); + app.UseMiddleware(); app.UseHttpsRedirection(); From 966ab9bdda8bcac890670fa3fa699e77d432fae6 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:19:40 +0300 Subject: [PATCH 025/126] feat: add generate and check token --- .../Configuration/SetupController.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Endpoint/Controllers/Configuration/SetupController.cs diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs new file mode 100644 index 0000000..225da6e --- /dev/null +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -0,0 +1,48 @@ +using System; +using System.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Mirea.Api.Endpoint.Common.Attributes; +using Mirea.Api.Endpoint.Common.Exceptions; +using Mirea.Api.Endpoint.Common.Interfaces; +using Mirea.Api.Endpoint.Configuration.General; + +namespace Mirea.Api.Endpoint.Controllers.Configuration; + +[ApiVersion("1.0")] +[ApiController] +[MaintenanceModeIgnore] +public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService) : BaseController +{ + [HttpGet("GenerateToken")] + public ActionResult GenerateToken() + { + if (!notConfigureService.IsMaintenanceMode) + throw new ControllerArgumentException( + "The token cannot be generated because the server has been configured. " + + $"If you need to restart the configuration, then delete the \"{GeneralConfig.FilePath}\" file and restart the application."); + + var token = new byte[32]; + RandomNumberGenerator.Create().GetBytes(token); + setupToken.SetToken(token); + + return Ok(Convert.ToBase64String(token)); + } + + [HttpGet("CheckToken")] + public ActionResult CheckToken([FromQuery] string token) + { + if (!setupToken.MatchToken(Convert.FromBase64String(token))) return Unauthorized("The token is not valid"); + + Response.Cookies.Append("AuthToken", token, new CookieOptions + { + HttpOnly = false, + Secure = false, + Path = "/" + }); + + return Ok(true); + } + + +} \ No newline at end of file From 9bf9eabad7d6a943276702c12b86fbece492f0dc Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 28 May 2024 07:20:21 +0300 Subject: [PATCH 026/126] fix: add full path to settings --- Endpoint/Controllers/Configuration/SetupController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 225da6e..c315237 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -20,7 +20,7 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur if (!notConfigureService.IsMaintenanceMode) throw new ControllerArgumentException( "The token cannot be generated because the server has been configured. " + - $"If you need to restart the configuration, then delete the \"{GeneralConfig.FilePath}\" file and restart the application."); + $"If you need to restart the configuration, then delete the \"{PathBuilder.Combine(GeneralConfig.FilePath)}\" file and restart the application."); var token = new byte[32]; RandomNumberGenerator.Create().GetBytes(token); From 22793c78826a86779230f4eafcad97e8fb2eb987 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:30:00 +0300 Subject: [PATCH 027/126] feat: add localhost attribute --- .../Common/Attributes/LocalhostAttribute.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Endpoint/Common/Attributes/LocalhostAttribute.cs diff --git a/Endpoint/Common/Attributes/LocalhostAttribute.cs b/Endpoint/Common/Attributes/LocalhostAttribute.cs new file mode 100644 index 0000000..734e0bf --- /dev/null +++ b/Endpoint/Common/Attributes/LocalhostAttribute.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Net; + +namespace Mirea.Api.Endpoint.Common.Attributes; + +public class LocalhostAttribute : ActionFilterAttribute +{ + public override void OnActionExecuting(ActionExecutingContext context) + { + var ip = context.HttpContext.Connection.RemoteIpAddress; + if (ip == null || !IPAddress.IsLoopback(ip)) + { + context.Result = new UnauthorizedResult(); + return; + } + base.OnActionExecuting(context); + } +} \ No newline at end of file From 07d7fec24fe9ee55798284ac45799866adad0109 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:30:26 +0300 Subject: [PATCH 028/126] feat: add localhost for generate token --- Endpoint/Controllers/Configuration/SetupController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index c315237..4317b95 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -15,6 +15,7 @@ namespace Mirea.Api.Endpoint.Controllers.Configuration; public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService) : BaseController { [HttpGet("GenerateToken")] + [Localhost] public ActionResult GenerateToken() { if (!notConfigureService.IsMaintenanceMode) From eefb049e0e22efdb358df16f5bac86daa001c64a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:35:52 +0300 Subject: [PATCH 029/126] feat: add cache for save intermediate settings --- .../Controllers/Configuration/SetupController.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 4317b95..b629952 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -2,18 +2,28 @@ using System.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; using Mirea.Api.Endpoint.Common.Attributes; using Mirea.Api.Endpoint.Common.Exceptions; using Mirea.Api.Endpoint.Common.Interfaces; +using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration.General; +using Mirea.Api.Endpoint.Configuration.General.Settings; namespace Mirea.Api.Endpoint.Controllers.Configuration; [ApiVersion("1.0")] [ApiController] [MaintenanceModeIgnore] -public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService) : BaseController +public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService, IMemoryCache cache) : BaseController { + private const string CacheGeneralKey = "config_part"; + private GeneralConfig GeneralConfig +{ + get => cache.Get(CacheGeneralKey) ?? new GeneralConfig(); + set => cache.Set(CacheGeneralKey, value); + } + [HttpGet("GenerateToken")] [Localhost] public ActionResult GenerateToken() From e7ed69169c5c1ddbcae5a1dbd53afb6c4ab7031f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:37:04 +0300 Subject: [PATCH 030/126] feat: add setter database --- .../Configuration/SetupController.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index b629952..1384d9b 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -9,6 +9,8 @@ using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Settings; +using System; +using System.Data; namespace Mirea.Api.Endpoint.Controllers.Configuration; @@ -55,5 +57,32 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return Ok(true); } + private ActionResult SetDatabase(string connectionString, DbSettings.DatabaseEnum databaseType) + where TConnection : class, IDbConnection, new() + where TException : Exception + { + try + { + using var connection = new TConnection(); + connection.ConnectionString = connectionString; + connection.Open(); + connection.Close(); + + var general = GeneralConfig; + general.DbSettings = new DbSettings + { + ConnectionStringSql = connectionString, + TypeDatabase = databaseType + }; + GeneralConfig = general; + + return Ok(true); + } + catch (TException ex) + { + throw new ControllerArgumentException($"Error when connecting: {ex.Message}"); + } + } + } \ No newline at end of file From ba8ccf8b7e4278b6cbeefd44566716d2c4608471 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:38:21 +0300 Subject: [PATCH 031/126] feat: add set database endpoints --- .../Requests/Configuration/DatabaseRequest.cs | 22 ++++++++ .../Configuration/SetupController.cs | 52 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 ApiDto/Requests/Configuration/DatabaseRequest.cs diff --git a/ApiDto/Requests/Configuration/DatabaseRequest.cs b/ApiDto/Requests/Configuration/DatabaseRequest.cs new file mode 100644 index 0000000..4308471 --- /dev/null +++ b/ApiDto/Requests/Configuration/DatabaseRequest.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests.Configuration; + +public class DatabaseRequest +{ + [Required] + public required string Server { get; set; } + + [Required] + public int Port { get; set; } + + [Required] + public required string Database { get; set; } + + [Required] + public required string User { get; set; } + + [Required] + public bool Ssl { get; set; } + public string? Password { get; set; } +} \ No newline at end of file diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 1384d9b..04924ac 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -84,5 +84,57 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur } } + [HttpPost("SetPsql")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetPsql([FromBody] DatabaseRequest request) + { + string connectionString = $"Host={request.Server}:{request.Port};Username={request.User};Database={request.Database}"; + if (request.Password != null) + connectionString += $";Password={request.Password}"; + if (request.Ssl) + connectionString += ";SSL Mode=Require;"; + + return SetDatabase(connectionString, DbSettings.DatabaseEnum.PostgresSql); + } + + [HttpPost("SetMysql")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetMysql([FromBody] DatabaseRequest request) + { + string connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database}"; + if (request.Password != null) + connectionString += $";Pwd={request.Password}"; + if (request.Ssl) + connectionString += ";SslMode=Require;"; + + return SetDatabase(connectionString, DbSettings.DatabaseEnum.Mysql); + } + + [HttpPost("SetSqlite")] + [TokenAuthentication] + public ActionResult SetSqlite([FromQuery] string? path) + { + if (string.IsNullOrEmpty(path)) path = "database"; + + path = PathBuilder.Combine(path); + + if (!Directory.Exists(path)) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + Directory.CreateDirectory(path); + else + Directory.CreateDirectory(path, UnixFileMode.UserRead | UnixFileMode.UserWrite); + } + else + throw new ControllerArgumentException("Such a folder exists. Enter a different name"); + + string connectionString = $"Data Source={PathBuilder.Combine(path, "database.db3")}"; + + return SetDatabase(connectionString, DbSettings.DatabaseEnum.Sqlite); + } + } + } \ No newline at end of file From 2d67a565cabec799622ac8eae804694c88828638 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:41:54 +0300 Subject: [PATCH 032/126] docs: add xml doc for request --- .../Requests/Configuration/DatabaseRequest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ApiDto/Requests/Configuration/DatabaseRequest.cs b/ApiDto/Requests/Configuration/DatabaseRequest.cs index 4308471..5da58e3 100644 --- a/ApiDto/Requests/Configuration/DatabaseRequest.cs +++ b/ApiDto/Requests/Configuration/DatabaseRequest.cs @@ -2,21 +2,43 @@ namespace Mirea.Api.Dto.Requests.Configuration; +/// +/// Represents a request to configure the database connection settings. +/// public class DatabaseRequest { + /// + /// Gets or sets the server address. + /// [Required] public required string Server { get; set; } + /// + /// Gets or sets the port number. + /// [Required] public int Port { get; set; } + /// + /// Gets or sets the database name. + /// [Required] public required string Database { get; set; } + /// + /// Gets or sets the username. + /// [Required] public required string User { get; set; } + /// + /// Gets or sets a value indicating whether SSL is enabled. + /// [Required] public bool Ssl { get; set; } + + /// + /// Gets or sets the password. + /// public string? Password { get; set; } } \ No newline at end of file From c02240077f5c3521a7ea44b31094bf2b321767a9 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:42:39 +0300 Subject: [PATCH 033/126] fix: add missing ref --- Endpoint/Controllers/Configuration/SetupController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 04924ac..43fb6c3 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -2,13 +2,18 @@ using System.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.Sqlite; using Microsoft.Extensions.Caching.Memory; +using Mirea.Api.Dto.Requests; +using Mirea.Api.Dto.Requests.Configuration; using Mirea.Api.Endpoint.Common.Attributes; using Mirea.Api.Endpoint.Common.Exceptions; using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Settings; +using MySqlConnector; +using Npgsql; using System; using System.Data; From 29c9c10a53e064c15011927b1a39ff771ab06ce0 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:43:08 +0300 Subject: [PATCH 034/126] feat: add endpoint for cache --- .../Configuration/SetupController.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 43fb6c3..ed8512a 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -14,6 +14,7 @@ using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Settings; using MySqlConnector; using Npgsql; +using StackExchange.Redis; using System; using System.Data; @@ -139,6 +140,52 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return SetDatabase(connectionString, DbSettings.DatabaseEnum.Sqlite); } + + [HttpPost("SetRedis")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetRedis([FromBody] CacheRequest request) + { + string connectionString = $"{request.Server}:{request.Port},ssl=false"; + if (request.Password != null) + connectionString += $",password={request.Password}"; + + try + { + var redis = ConnectionMultiplexer.Connect(connectionString); + redis.Close(); + + var general = GeneralConfig; + general.CacheSettings = new CacheSettings + { + ConnectionString = connectionString, + TypeDatabase = CacheSettings.CacheEnum.Redis + }; + GeneralConfig = general; + + return Ok(true); + } + catch (Exception ex) + { + throw new ControllerArgumentException("Error when connecting to Redis: " + ex.Message); + } + } + + [HttpPost("SetMemcached")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetMemcached() + { + var general = GeneralConfig; + general.CacheSettings = new CacheSettings + { + ConnectionString = null, + TypeDatabase = CacheSettings.CacheEnum.Memcached + }; + GeneralConfig = general; + + return Ok(true); + } } From f5deeec6c94282dc2191230828e7c1ed266c8c75 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:44:24 +0300 Subject: [PATCH 035/126] feat: add endpoint for logging --- .../Requests/Configuration/LoggingRequest.cs | 25 +++++++++++++++++ .../Configuration/SetupController.cs | 28 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 ApiDto/Requests/Configuration/LoggingRequest.cs diff --git a/ApiDto/Requests/Configuration/LoggingRequest.cs b/ApiDto/Requests/Configuration/LoggingRequest.cs new file mode 100644 index 0000000..eba6428 --- /dev/null +++ b/ApiDto/Requests/Configuration/LoggingRequest.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests.Configuration; + +/// +/// Represents a request to configure logging settings. +/// +public class LoggingRequest +{ + /// + /// Gets or sets a value indicating whether logging to file is enabled. + /// + [Required] + public bool EnableLogToFile { get; set; } + + /// + /// Gets or sets the log file name. + /// + public string? LogFileName { get; set; } + + /// + /// Gets or sets the log file path. + /// + public string? LogFilePath { get; set; } +} \ No newline at end of file diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index ed8512a..ccc51be 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -186,6 +186,34 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return Ok(true); } + + [HttpPost("SetLogging")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetLogging([FromBody] LoggingRequest? request) + { + var settings = (request == null) switch + { + true => new LogSettings + { + EnableLogToFile = true, + LogFileName = "logging-", + LogFilePath = "logs" + }, + false => new LogSettings + { + EnableLogToFile = request.EnableLogToFile, + LogFileName = request.LogFileName, + LogFilePath = request.LogFilePath + } + }; + + var general = GeneralConfig; + general.LogSettings = settings; + GeneralConfig = general; + + return true; + } } From 5d308f1a242024ca4706e9d91c5d157756c6815e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:44:39 +0300 Subject: [PATCH 036/126] feat: add request for cache --- ApiDto/Requests/Configuration/CacheRequest.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 ApiDto/Requests/Configuration/CacheRequest.cs diff --git a/ApiDto/Requests/Configuration/CacheRequest.cs b/ApiDto/Requests/Configuration/CacheRequest.cs new file mode 100644 index 0000000..cafc1c6 --- /dev/null +++ b/ApiDto/Requests/Configuration/CacheRequest.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests.Configuration; + +/// +/// Represents a request to configure cache settings. +/// +public class CacheRequest +{ + /// + /// Gets or sets the server address. + /// + [Required] + public required string Server { get; set; } + + /// + /// Gets or sets the port number. + /// + [Required] + public int Port { get; set; } + + /// + /// Gets or sets the password. + /// + public string? Password { get; set; } +} \ No newline at end of file From 17961ccefc782ad8782eaeb10297281731c0a459 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:45:02 +0300 Subject: [PATCH 037/126] feat: add email endpoint --- ApiDto/Requests/Configuration/EmailRequest.cs | 45 +++++++++++++++++++ .../Configuration/SetupController.cs | 26 +++++++++++ 2 files changed, 71 insertions(+) create mode 100644 ApiDto/Requests/Configuration/EmailRequest.cs diff --git a/ApiDto/Requests/Configuration/EmailRequest.cs b/ApiDto/Requests/Configuration/EmailRequest.cs new file mode 100644 index 0000000..7770c6e --- /dev/null +++ b/ApiDto/Requests/Configuration/EmailRequest.cs @@ -0,0 +1,45 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests.Configuration; + +/// +/// Represents a request to configure email settings. +/// +public class EmailRequest +{ + /// + /// Gets or sets the server address. + /// + [Required] + public required string Server { get; set; } + + /// + /// Gets or sets the email address from which emails will be sent. + /// + [Required] + public required string From { get; set; } + + /// + /// Gets or sets the password for the email account. + /// + [Required] + public required string Password { get; set; } + + /// + /// Gets or sets the port number. + /// + [Required] + public int Port { get; set; } + + /// + /// Gets or sets a value indicating whether SSL is enabled. + /// + [Required] + public bool Ssl { get; set; } + + /// + /// Gets or sets the username. + /// + [Required] + public required string User { get; set; } +} \ No newline at end of file diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index ccc51be..d5eb17e 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -214,6 +214,32 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return true; } + + [HttpPost("SetEmail")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetEmail([FromBody] EmailRequest? request) + { + var settings = (request == null) switch + { + true => new EmailSettings(), + false => new EmailSettings + { + Server = request.Server, + From = request.From, + Password = request.Password, + Port = request.Port, + Ssl = request.Ssl, + User = request.User + } + }; + + var general = GeneralConfig; + general.EmailSettings = settings; + GeneralConfig = general; + + return true; + } } From f9750ef039bcb17c266878e478e221202eedf320 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:46:16 +0300 Subject: [PATCH 038/126] feat: add endpoint for schedule --- .../ScheduleConfigurationRequest.cs | 29 +++++++++++++++++++ .../Configuration/SetupController.cs | 28 ++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 ApiDto/Requests/Configuration/ScheduleConfigurationRequest.cs diff --git a/ApiDto/Requests/Configuration/ScheduleConfigurationRequest.cs b/ApiDto/Requests/Configuration/ScheduleConfigurationRequest.cs new file mode 100644 index 0000000..ca6e950 --- /dev/null +++ b/ApiDto/Requests/Configuration/ScheduleConfigurationRequest.cs @@ -0,0 +1,29 @@ +using Mirea.Api.Dto.Common; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests.Configuration; + +/// +/// Represents a request to configure the schedule settings. +/// +public class ScheduleConfigurationRequest +{ + /// + /// Gets or sets the cron expression for updating the schedule. + /// + public string? CronUpdateSchedule { get; set; } + + /// + /// Gets or sets the start date of the term. + /// + [Required] + public DateOnly StartTerm { get; set; } + + /// + /// Gets or sets the pair period times, keyed by pair number. + /// + [Required] + public required IDictionary PairPeriod { get; set; } +} \ No newline at end of file diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index d5eb17e..7abd5ee 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -1,5 +1,4 @@ -using System; -using System.Security.Cryptography; +using Cronos; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; @@ -17,6 +16,10 @@ using Npgsql; using StackExchange.Redis; using System; using System.Data; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text.Json; namespace Mirea.Api.Endpoint.Controllers.Configuration; @@ -240,6 +243,27 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return true; } + + [HttpPost("SetSchedule")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult SetSchedule([FromBody] ScheduleConfigurationRequest request) + { + var general = GeneralConfig; + general.ScheduleSettings = new ScheduleSettings + { + // every 6 hours + CronUpdateSchedule = request.CronUpdateSchedule ?? "0 */6 * * *", + StartTerm = request.StartTerm, + PairPeriod = request.PairPeriod.ConvertFromDto() + }; + + if (!CronExpression.TryParse(general.ScheduleSettings.CronUpdateSchedule, CronFormat.Standard, out _)) + throw new ControllerArgumentException("The Cron task could not be parsed. Check the format of the entered data."); + + GeneralConfig = general; + + return true; } From e1ad287da1131c89f3d6b169a8d768e304567f3c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:47:55 +0300 Subject: [PATCH 039/126] build: add missing reference --- Endpoint/Endpoint.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Endpoint/Endpoint.csproj b/Endpoint/Endpoint.csproj index 0191a2e..a1e08aa 100644 --- a/Endpoint/Endpoint.csproj +++ b/Endpoint/Endpoint.csproj @@ -22,8 +22,9 @@ - + + From c427006283c824d21b29f9d2b56f796eb87edc07 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 03:52:31 +0300 Subject: [PATCH 040/126] docs: add env data --- .env | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/.env b/.env index af67fc3..f06ff63 100644 --- a/.env +++ b/.env @@ -16,4 +16,86 @@ # databases (if Sqlite) and other data that should be saved in a place other than the place where the program is launched. # REQUIRED if the application is inside the container # If you want to change this value, you need to change the values in Settings.json and move the file itself to the desired location. -PATH_TO_SAVE= \ No newline at end of file +PATH_TO_SAVE= + +# Security + +# JWT signature token +# string (UTF8) +# This token will be used to create and verify the signature of JWT tokens. +# The token must be equal to 64 characters +SECURITY_SIGNING_TOKEN= + +# Token for JWT encryption +# string (UTF8) +# This token will be used to encrypt and decrypt JWT tokens. +# The token must be equal to 32 characters +SECURITY_ENCRYPTION_TOKEN= + +# Time in minutes, which indicates after which time the Refresh Token will become invalid +# integer +# The token indicates how long after the user is inactive, he will need to log in again +SECURITY_LIFE_TIME_RT=1440 + +# The time in a minute, which indicates that this is exactly what it takes to become a non-state +# integer +# Do not specify a time that is too long or too short. Optimally 5 > x > 60 +SECURITY_LIFE_TIME_JWT=15 + +# Time in minutes, which indicates after which time the token of the first factor will become invalid +# integer +# Do not specify a short time. The user must be able to log in using the second factor +SECURITY_LIFE_TIME_1_FA=15 + +# An identifier that points to the server that created the token +# string +SECURITY_JWT_ISSUER= + +# ID of the audience for which the token is intended +# string +SECURITY_JWT_AUDIENCE= + +### Hashing + +# In order to set up hashing correctly, you need to start from the security requirements +# You can use the settings that were used in https://github.com/P-H-C/phc-winner-argon2 +# These parameters have a STRONG impact on performance +# When testing the system, these values were used: +# 10 <= SECURITY_HASH_ITERATION <= 25 iterations +# 16384 <= SECURITY_HASH_MEMORY <= 32768 KB +# 4 <= SECURITY_HASH_PARALLELISM <= 8 lines +# If we take all the large values, it will take a little more than 1 second to get the hash. If this time is critical, reduce the parameters + +# The number of iterations used to hash passwords in the Argon2 algorithm +# integer +# This parameter determines the number of iterations that the Argon2 algorithm goes through when hashing passwords. +# Increasing this value can improve security by increasing the time it takes to calculate the password hash. +# The average number of iterations to increase the security level should be set to at least 10. +SECURITY_HASH_ITERATION= + +# The amount of memory used to hash passwords in the Argon2 algorithm +# integer +# 65536 +# This parameter determines the number of kilobytes of memory that will be used for the password hashing process. +# Increasing this value may increase security, but it may also require more system resources. +SECURITY_HASH_MEMORY= + +# Parallelism determines how many of the memory fragments divided into strips will be used to generate a hash +# integer +# This value affects the hash itself, but can be changed to achieve an ideal execution time, taking into account the processor and the number of cores. +SECURITY_HASH_PARALLELISM= + +# The size of the output hash generated by the password hashing algorithm +# integer +SECURITY_HASH_SIZE=32 + +# Additional protection for Argon2 +# string (BASE64) +# (optional) +# We recommend installing a token so that even if the data is compromised, an attacker cannot brute force a password without a token +SECURITY_HASH_TOKEN= + +# The size of the salt used to hash passwords +# integer +# The salt is a random value added to the password before hashing to prevent the use of rainbow hash tables and other attacks. +SECURITY_SALT_SIZE=16 \ No newline at end of file From 7e283fe6437f90eb9b2529541399884d93fc6047 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:03:20 +0300 Subject: [PATCH 041/126] feat: add security layer --- Security/Security.csproj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Security/Security.csproj diff --git a/Security/Security.csproj b/Security/Security.csproj new file mode 100644 index 0000000..94ec65a --- /dev/null +++ b/Security/Security.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + disable + enable + Winsomnia + 1.0.0-a0 + 1.0.0.0 + 1.0.0.0 + Mirea.Api.Security + $(AssemblyName) + + + From 930edd4c2c83f36185d3b165c7fa9c35a68f2687 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:03:47 +0300 Subject: [PATCH 042/126] build: add ref --- Security/Security.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Security/Security.csproj b/Security/Security.csproj index 94ec65a..d9e1953 100644 --- a/Security/Security.csproj +++ b/Security/Security.csproj @@ -12,4 +12,8 @@ $(AssemblyName) + + + + From e1123cf36b98515a841199b7a5b18b7e7f91e1a4 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:04:02 +0300 Subject: [PATCH 043/126] feat: add password hashing --- Security/PasswordHashService.cs | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Security/PasswordHashService.cs diff --git a/Security/PasswordHashService.cs b/Security/PasswordHashService.cs new file mode 100644 index 0000000..a299901 --- /dev/null +++ b/Security/PasswordHashService.cs @@ -0,0 +1,76 @@ +using System.Buffers.Text; +using System.Text; +using Konscious.Security.Cryptography; + +namespace Security; + +public class PasswordHashService +{ + public int SaltSize { private get; init; } + public int HashSize { private get; init; } + public int Iterations { private get; init; } + public int Memory { private get; init; } + public int Parallelism { private get; init; } + public string? Secret { private get; init; } + + private ReadOnlySpan HashPassword(string password, ReadOnlySpan salt) + { + var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password)) + { + Iterations = Iterations, + MemorySize = Memory, + DegreeOfParallelism = Parallelism, + Salt = salt.ToArray() + }; + + if (!string.IsNullOrEmpty(Secret)) + argon2.KnownSecret = Convert.FromBase64String(Secret); + + return argon2.GetBytes(HashSize); + } + + private static bool ConstantTimeComparison(ReadOnlySpan a, ReadOnlySpan b) + { + if (a.Length != b.Length) + return false; + + int result = 0; + for (int i = 0; i < a.Length; i++) + result |= a[i] ^ b[i]; + return result == 0; + } + + public static ReadOnlySpan GenerateRandomKeyBytes(int size) + { + var key = new byte[size]; + using var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); + rng.GetNonZeroBytes(key); + return key; + } + + public static string GenerateRandomKeyStringBase64(int size) => + Convert.ToBase64String(GenerateRandomKeyBytes(size)); + + public static string GenerateRandomKeyString(int size) + { + var randomBytes = GenerateRandomKeyBytes(size); + Span utf8Bytes = new byte[Base64.GetMaxEncodedToUtf8Length(randomBytes.Length)]; + + Base64.EncodeToUtf8(randomBytes, utf8Bytes, out _, out _); + return Encoding.UTF8.GetString(utf8Bytes); + } + + public (string Salt, string Hash) HashPassword(string password) + { + var salt = GenerateRandomKeyBytes(SaltSize); + var hash = HashPassword(password, salt); + + return (Convert.ToBase64String(salt), Convert.ToBase64String(hash)); + } + + public bool VerifyPassword(string password, ReadOnlySpan salt, ReadOnlySpan hash) => + ConstantTimeComparison(HashPassword(password, salt), hash); + + public bool VerifyPassword(string password, string saltBase64, string hashBase64) => + VerifyPassword(password, Convert.FromBase64String(saltBase64), Convert.FromBase64String(hashBase64)); +} \ No newline at end of file From 3149f50586cb54dde41731623f6e1dcb7c4ff72d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:05:18 +0300 Subject: [PATCH 044/126] refactor: move class to correct namespace --- Security/PasswordHashService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Security/PasswordHashService.cs b/Security/PasswordHashService.cs index a299901..9ea24b3 100644 --- a/Security/PasswordHashService.cs +++ b/Security/PasswordHashService.cs @@ -1,8 +1,9 @@ -using System.Buffers.Text; +using Konscious.Security.Cryptography; +using System; +using System.Buffers.Text; using System.Text; -using Konscious.Security.Cryptography; -namespace Security; +namespace Mirea.Api.Security; public class PasswordHashService { From e3dd0a84190cf3414e5639d912f78a8e793952e8 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:08:51 +0300 Subject: [PATCH 045/126] build: add ref for DI --- Security/Security.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Security/Security.csproj b/Security/Security.csproj index d9e1953..218d9f6 100644 --- a/Security/Security.csproj +++ b/Security/Security.csproj @@ -14,6 +14,8 @@ + + From 656d7dca0baa92246884fe31f4b27d1e6bffb482 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:09:10 +0300 Subject: [PATCH 046/126] feat: add DI --- Security/DependencyInjection.cs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Security/DependencyInjection.cs diff --git a/Security/DependencyInjection.cs b/Security/DependencyInjection.cs new file mode 100644 index 0000000..4dbab98 --- /dev/null +++ b/Security/DependencyInjection.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Mirea.Api.Security; + +public static class DependencyInjection +{ + public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration) + { + var saltSize = int.Parse(configuration["SECURITY_SALT_SIZE"]!); + var hashSize = int.Parse(configuration["SECURITY_HASH_SIZE"]!); + var iteration = int.Parse(configuration["SECURITY_HASH_ITERATION"]!); + var memory = int.Parse(configuration["SECURITY_HASH_MEMORY"]!); + var parallelism = int.Parse(configuration["SECURITY_HASH_PARALLELISM"]!); + + services.AddSingleton(new PasswordHashService + { + SaltSize = saltSize, + HashSize = hashSize, + Iterations = iteration, + Memory = memory, + Parallelism = parallelism, + Secret = configuration["SECURITY_HASH_TOKEN"] + }); + + return services; + } +} \ No newline at end of file From 6029ea3c2cc20ebfa1958ce5524d5db83b82e25a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:11:04 +0300 Subject: [PATCH 047/126] refactor: move hashing to services --- Security/DependencyInjection.cs | 3 ++- Security/{ => Services}/PasswordHashService.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename Security/{ => Services}/PasswordHashService.cs (98%) diff --git a/Security/DependencyInjection.cs b/Security/DependencyInjection.cs index 4dbab98..39e113a 100644 --- a/Security/DependencyInjection.cs +++ b/Security/DependencyInjection.cs @@ -1,11 +1,12 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Mirea.Api.Security.Services; namespace Mirea.Api.Security; public static class DependencyInjection { - public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddSecurityServices(this IServiceCollection services, IConfiguration configuration) { var saltSize = int.Parse(configuration["SECURITY_SALT_SIZE"]!); var hashSize = int.Parse(configuration["SECURITY_HASH_SIZE"]!); diff --git a/Security/PasswordHashService.cs b/Security/Services/PasswordHashService.cs similarity index 98% rename from Security/PasswordHashService.cs rename to Security/Services/PasswordHashService.cs index 9ea24b3..b9cc41c 100644 --- a/Security/PasswordHashService.cs +++ b/Security/Services/PasswordHashService.cs @@ -3,7 +3,7 @@ using System; using System.Buffers.Text; using System.Text; -namespace Mirea.Api.Security; +namespace Mirea.Api.Security.Services; public class PasswordHashService { From f749ed42f5ba34458a238db9befbc7a66ec4545e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:29:50 +0300 Subject: [PATCH 048/126] feat: add interface for save to cache --- Security/Common/Interfaces/ICacheService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Security/Common/Interfaces/ICacheService.cs diff --git a/Security/Common/Interfaces/ICacheService.cs b/Security/Common/Interfaces/ICacheService.cs new file mode 100644 index 0000000..c2a419b --- /dev/null +++ b/Security/Common/Interfaces/ICacheService.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Mirea.Api.Security.Common.Interfaces; + +public interface ICacheService +{ + Task SetAsync(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, CancellationToken cancellationToken = default); + Task GetAsync(string key, CancellationToken cancellationToken = default); + Task RemoveAsync(string key, CancellationToken cancellationToken = default); +} From 58ceca5313fa714f0418be3142c697269a7691b7 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:30:32 +0300 Subject: [PATCH 049/126] feat: add pre-auth token structure --- Security/Common/Domain/PreAuthToken.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Security/Common/Domain/PreAuthToken.cs diff --git a/Security/Common/Domain/PreAuthToken.cs b/Security/Common/Domain/PreAuthToken.cs new file mode 100644 index 0000000..4da9f6d --- /dev/null +++ b/Security/Common/Domain/PreAuthToken.cs @@ -0,0 +1,9 @@ +namespace Mirea.Api.Security.Common.Domain; + +public class PreAuthToken +{ + public required string Fingerprint { get; set; } + public required string UserAgent { get; set; } + public required string UserId { get; set; } + public required string Token { get; set; } +} \ No newline at end of file From e3db6b73e01fb86d983074190b3b27111beddf7c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:30:55 +0300 Subject: [PATCH 050/126] feat: add pre-auth response --- Security/Common/Dto/Responses/PreAuthTokenResponse.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Security/Common/Dto/Responses/PreAuthTokenResponse.cs diff --git a/Security/Common/Dto/Responses/PreAuthTokenResponse.cs b/Security/Common/Dto/Responses/PreAuthTokenResponse.cs new file mode 100644 index 0000000..9a7238f --- /dev/null +++ b/Security/Common/Dto/Responses/PreAuthTokenResponse.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mirea.Api.Security.Common.Dto.Responses; + +public class PreAuthTokenResponse +{ + public required string Token { get; set; } + public DateTime ExpiresIn { get; set; } +} \ No newline at end of file From 3c9694de08a9870453371f645f7616c6e8af0805 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:31:19 +0300 Subject: [PATCH 051/126] feat: add request for get token --- Security/Common/Dto/Requests/TokenRequest.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Security/Common/Dto/Requests/TokenRequest.cs diff --git a/Security/Common/Dto/Requests/TokenRequest.cs b/Security/Common/Dto/Requests/TokenRequest.cs new file mode 100644 index 0000000..8be8038 --- /dev/null +++ b/Security/Common/Dto/Requests/TokenRequest.cs @@ -0,0 +1,8 @@ +namespace Mirea.Api.Security.Common.Dto.Requests; + +public class TokenRequest +{ + public required string Fingerprint { get; set; } + public required string UserAgent { get; set; } + public required string Ip { get; set; } +} \ No newline at end of file From b14ae26a4832ec8f41f43df58655e39f6151843d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:31:47 +0300 Subject: [PATCH 052/126] feat: add pre-auth service --- Security/Services/PreAuthService.cs | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Security/Services/PreAuthService.cs diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs new file mode 100644 index 0000000..2a1929c --- /dev/null +++ b/Security/Services/PreAuthService.cs @@ -0,0 +1,42 @@ +using Mirea.Api.Security.Common.Domain; +using Mirea.Api.Security.Common.Dto.Requests; +using Mirea.Api.Security.Common.Dto.Responses; +using Mirea.Api.Security.Common.Interfaces; +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Mirea.Api.Security.Services; + +public class PreAuthService(ICacheService cache) +{ + public TimeSpan Lifetime { private get; init; } + + private static string GenerateFirstAuthToken() => Guid.NewGuid().ToString().Replace("-", ""); + + public async Task CreateLoginTokenAsync(TokenRequest request, string userId, CancellationToken cancellation = default) + { + var firstAuthToken = GenerateFirstAuthToken(); + + var loginStructure = new PreAuthToken + { + Fingerprint = request.Fingerprint, + UserId = userId, + UserAgent = request.UserAgent, + Token = firstAuthToken + }; + + await cache.SetAsync( + request.Fingerprint, + JsonSerializer.SerializeToUtf8Bytes(loginStructure), + Lifetime, + cancellation); + + return new PreAuthTokenResponse + { + Token = firstAuthToken, + ExpiresIn = DateTime.UtcNow.Add(Lifetime) + }; + } +} \ No newline at end of file From 8408b80c35258aceba37ae9662407779957566c3 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:34:00 +0300 Subject: [PATCH 053/126] feat: add pre-auth to DI --- Security/DependencyInjection.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Security/DependencyInjection.cs b/Security/DependencyInjection.cs index 39e113a..5441ebc 100644 --- a/Security/DependencyInjection.cs +++ b/Security/DependencyInjection.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Mirea.Api.Security.Common.Interfaces; using Mirea.Api.Security.Services; +using System; namespace Mirea.Api.Security; @@ -24,6 +26,18 @@ public static class DependencyInjection Secret = configuration["SECURITY_HASH_TOKEN"] }); + var lifeTimeLogin = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_1_FA"]!)); + + services.AddSingleton(provider => + { + var cache = provider.GetRequiredService(); + + return new PreAuthService(cache) + { + Lifetime = lifeTimeLogin + }; + }); + return services; } } \ No newline at end of file From 5fde5bd3967f0ce86a7490ab3321364add1feb2a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:34:39 +0300 Subject: [PATCH 054/126] build: add security to sln --- Backend.sln | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Backend.sln b/Backend.sln index 95ebfb7..5790acf 100644 --- a/Backend.sln +++ b/Backend.sln @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "Persistence\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiDto", "ApiDto\ApiDto.csproj", "{0335FA36-E137-453F-853B-916674C168FE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Security", "Security\Security.csproj", "{47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,10 @@ Global {0335FA36-E137-453F-853B-916674C168FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {0335FA36-E137-453F-853B-916674C168FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {0335FA36-E137-453F-853B-916674C168FE}.Release|Any CPU.Build.0 = Release|Any CPU + {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d05ba5349fd44eeef83335584d28bd9c2a4a08df Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:48:37 +0300 Subject: [PATCH 055/126] refactor: isolate key generation --- Security/Services/GeneratorKey.cs | 28 ++++++++++++++++++++++++ Security/Services/PasswordHashService.cs | 23 +------------------ 2 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 Security/Services/GeneratorKey.cs diff --git a/Security/Services/GeneratorKey.cs b/Security/Services/GeneratorKey.cs new file mode 100644 index 0000000..79a0430 --- /dev/null +++ b/Security/Services/GeneratorKey.cs @@ -0,0 +1,28 @@ +using System; +using System.Buffers.Text; +using System.Text; + +namespace Mirea.Api.Security.Services; + +public static class GeneratorKey +{ + public static ReadOnlySpan GenerateBytes(int size) + { + var key = new byte[size]; + using var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); + rng.GetNonZeroBytes(key); + return key; + } + + public static string GenerateBase64(int size) => + Convert.ToBase64String(GenerateBytes(size)); + + public static string GenerateString(int size) + { + var randomBytes = GenerateBytes(size); + Span utf8Bytes = new byte[Base64.GetMaxEncodedToUtf8Length(randomBytes.Length)]; + + Base64.EncodeToUtf8(randomBytes, utf8Bytes, out _, out _); + return Encoding.UTF8.GetString(utf8Bytes); + } +} \ No newline at end of file diff --git a/Security/Services/PasswordHashService.cs b/Security/Services/PasswordHashService.cs index b9cc41c..8673222 100644 --- a/Security/Services/PasswordHashService.cs +++ b/Security/Services/PasswordHashService.cs @@ -1,6 +1,5 @@ using Konscious.Security.Cryptography; using System; -using System.Buffers.Text; using System.Text; namespace Mirea.Api.Security.Services; @@ -41,29 +40,9 @@ public class PasswordHashService return result == 0; } - public static ReadOnlySpan GenerateRandomKeyBytes(int size) - { - var key = new byte[size]; - using var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); - rng.GetNonZeroBytes(key); - return key; - } - - public static string GenerateRandomKeyStringBase64(int size) => - Convert.ToBase64String(GenerateRandomKeyBytes(size)); - - public static string GenerateRandomKeyString(int size) - { - var randomBytes = GenerateRandomKeyBytes(size); - Span utf8Bytes = new byte[Base64.GetMaxEncodedToUtf8Length(randomBytes.Length)]; - - Base64.EncodeToUtf8(randomBytes, utf8Bytes, out _, out _); - return Encoding.UTF8.GetString(utf8Bytes); - } - public (string Salt, string Hash) HashPassword(string password) { - var salt = GenerateRandomKeyBytes(SaltSize); + var salt = GeneratorKey.GenerateBytes(SaltSize); var hash = HashPassword(password, salt); return (Convert.ToBase64String(salt), Convert.ToBase64String(hash)); From 47a57693f86d8f64329b35dc7eef992071d47c8d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:55:34 +0300 Subject: [PATCH 056/126] sec: complicate the token --- Security/Services/PreAuthService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index 2a1929c..21c97ff 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -13,7 +13,8 @@ public class PreAuthService(ICacheService cache) { public TimeSpan Lifetime { private get; init; } - private static string GenerateFirstAuthToken() => Guid.NewGuid().ToString().Replace("-", ""); + private static string GeneratePreAuthToken() => Guid.NewGuid().ToString().Replace("-", "") + + GeneratorKey.GenerateString(16); public async Task CreateLoginTokenAsync(TokenRequest request, string userId, CancellationToken cancellation = default) { From ac7bbde75e07774428ce8d37d382629ffd937765 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:57:44 +0300 Subject: [PATCH 057/126] fix: add key for save pre auth token --- Security/Services/PreAuthService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index 21c97ff..484c5a1 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -16,7 +16,7 @@ public class PreAuthService(ICacheService cache) private static string GeneratePreAuthToken() => Guid.NewGuid().ToString().Replace("-", "") + GeneratorKey.GenerateString(16); - public async Task CreateLoginTokenAsync(TokenRequest request, string userId, CancellationToken cancellation = default) + private static string GetPreAuthCacheKey(string fingerprint) => $"{fingerprint}_pre_auth_token"; { var firstAuthToken = GenerateFirstAuthToken(); @@ -29,7 +29,7 @@ public class PreAuthService(ICacheService cache) }; await cache.SetAsync( - request.Fingerprint, + GetPreAuthCacheKey(request.Fingerprint), JsonSerializer.SerializeToUtf8Bytes(loginStructure), Lifetime, cancellation); From f4ad1518ef5cbae22c0bc4531c8a2742cdf1d40f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 04:58:21 +0300 Subject: [PATCH 058/126] style: rename variables --- Security/Services/PreAuthService.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index 484c5a1..cc189dd 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -17,26 +17,28 @@ public class PreAuthService(ICacheService cache) GeneratorKey.GenerateString(16); private static string GetPreAuthCacheKey(string fingerprint) => $"{fingerprint}_pre_auth_token"; - { - var firstAuthToken = GenerateFirstAuthToken(); - var loginStructure = new PreAuthToken + public async Task GeneratePreAuthTokenAsync(TokenRequest request, string userId, CancellationToken cancellation = default) + { + var preAuthToken = GeneratePreAuthToken(); + + var preAuthTokenStruct = new PreAuthToken { Fingerprint = request.Fingerprint, UserId = userId, UserAgent = request.UserAgent, - Token = firstAuthToken + Token = preAuthToken }; await cache.SetAsync( GetPreAuthCacheKey(request.Fingerprint), - JsonSerializer.SerializeToUtf8Bytes(loginStructure), + JsonSerializer.SerializeToUtf8Bytes(preAuthTokenStruct), Lifetime, cancellation); return new PreAuthTokenResponse { - Token = firstAuthToken, + Token = preAuthToken, ExpiresIn = DateTime.UtcNow.Add(Lifetime) }; } From 916b3795ed7fc62414e23d9657d76f102ea3bb1e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:27:27 +0300 Subject: [PATCH 059/126] feat: add ip to struct --- Security/Common/Domain/PreAuthToken.cs | 1 + Security/Services/PreAuthService.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Security/Common/Domain/PreAuthToken.cs b/Security/Common/Domain/PreAuthToken.cs index 4da9f6d..f1f9684 100644 --- a/Security/Common/Domain/PreAuthToken.cs +++ b/Security/Common/Domain/PreAuthToken.cs @@ -5,5 +5,6 @@ public class PreAuthToken public required string Fingerprint { get; set; } public required string UserAgent { get; set; } public required string UserId { get; set; } + public required string Ip { get; set; } public required string Token { get; set; } } \ No newline at end of file diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index cc189dd..2bb5390 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -27,7 +27,8 @@ public class PreAuthService(ICacheService cache) Fingerprint = request.Fingerprint, UserId = userId, UserAgent = request.UserAgent, - Token = preAuthToken + Token = preAuthToken, + Ip = request.Ip }; await cache.SetAsync( From 470031af39fb36c4068a36d87b2f46822243e27a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:27:49 +0300 Subject: [PATCH 060/126] feat: add match token --- Security/Services/PreAuthService.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index 2bb5390..1a0443e 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -3,6 +3,7 @@ using Mirea.Api.Security.Common.Dto.Requests; using Mirea.Api.Security.Common.Dto.Responses; using Mirea.Api.Security.Common.Interfaces; using System; +using System.Security; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -43,4 +44,21 @@ public class PreAuthService(ICacheService cache) ExpiresIn = DateTime.UtcNow.Add(Lifetime) }; } + public async Task MatchToken(TokenRequest request, string preAuthToken, CancellationToken cancellation = default) + { + var preAuthTokenStruct = await cache.GetAsync(GetPreAuthCacheKey(request.Fingerprint), cancellation) + ?? throw new SecurityException($"The token was not found using fingerprint \"{request.Fingerprint}\""); + + if (preAuthTokenStruct == null || + preAuthTokenStruct.Token != preAuthToken || + (preAuthTokenStruct.UserAgent != request.UserAgent && + preAuthTokenStruct.Ip != request.Ip)) + { + throw new SecurityException("It was not possible to verify the authenticity of the token"); + } + + await cache.RemoveAsync(GetPreAuthCacheKey(request.Fingerprint), cancellation); + + return preAuthTokenStruct.UserId; + } } \ No newline at end of file From d3a60d2a30f12d26c9212d8508d2e6f768a222dc Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:29:25 +0300 Subject: [PATCH 061/126] feat: add interface for gen access token --- Security/Common/Interfaces/IAccessToken.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Security/Common/Interfaces/IAccessToken.cs diff --git a/Security/Common/Interfaces/IAccessToken.cs b/Security/Common/Interfaces/IAccessToken.cs new file mode 100644 index 0000000..a2ebed2 --- /dev/null +++ b/Security/Common/Interfaces/IAccessToken.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mirea.Api.Security.Common.Interfaces; + +public interface IAccessToken +{ + (string Token, DateTime ExpireIn) GenerateToken(string userId); + DateTimeOffset GetExpireDateTime(string token); +} \ No newline at end of file From f55d701ff330331bc635187f8163901f28b9ebac Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:30:00 +0300 Subject: [PATCH 062/126] feat: add sliding expiration for cache --- Security/Common/Interfaces/ICacheService.cs | 6 +++++- Security/Services/PreAuthService.cs | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Security/Common/Interfaces/ICacheService.cs b/Security/Common/Interfaces/ICacheService.cs index c2a419b..c2cb1e3 100644 --- a/Security/Common/Interfaces/ICacheService.cs +++ b/Security/Common/Interfaces/ICacheService.cs @@ -6,7 +6,11 @@ namespace Mirea.Api.Security.Common.Interfaces; public interface ICacheService { - Task SetAsync(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, CancellationToken cancellationToken = default); + Task SetAsync(string key, T value, + TimeSpan? absoluteExpirationRelativeToNow = null, + TimeSpan? slidingExpiration = null, + CancellationToken cancellationToken = default); + Task GetAsync(string key, CancellationToken cancellationToken = default); Task RemoveAsync(string key, CancellationToken cancellationToken = default); } diff --git a/Security/Services/PreAuthService.cs b/Security/Services/PreAuthService.cs index 1a0443e..948b997 100644 --- a/Security/Services/PreAuthService.cs +++ b/Security/Services/PreAuthService.cs @@ -14,7 +14,7 @@ public class PreAuthService(ICacheService cache) { public TimeSpan Lifetime { private get; init; } - private static string GeneratePreAuthToken() => Guid.NewGuid().ToString().Replace("-", "") + + private static string GeneratePreAuthToken() => Guid.NewGuid().ToString().Replace("-", "") + GeneratorKey.GenerateString(16); private static string GetPreAuthCacheKey(string fingerprint) => $"{fingerprint}_pre_auth_token"; @@ -35,8 +35,8 @@ public class PreAuthService(ICacheService cache) await cache.SetAsync( GetPreAuthCacheKey(request.Fingerprint), JsonSerializer.SerializeToUtf8Bytes(preAuthTokenStruct), - Lifetime, - cancellation); + absoluteExpirationRelativeToNow: Lifetime, + cancellationToken: cancellation); return new PreAuthTokenResponse { From 7df4c8e4b6d5f97a32cc8099b65fc4b4a888de5e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:32:22 +0300 Subject: [PATCH 063/126] feat: add auth service --- Security/Services/AuthService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Security/Services/AuthService.cs diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs new file mode 100644 index 0000000..97aca20 --- /dev/null +++ b/Security/Services/AuthService.cs @@ -0,0 +1,8 @@ +using System; + +namespace Mirea.Api.Security.Services; + +public class AuthService() +{ + public TimeSpan Lifetime { private get; init; } +} \ No newline at end of file From b25be758addb7e5207be39e5e7a28a2971cbb706 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:33:55 +0300 Subject: [PATCH 064/126] feat: add auth token --- Security/Common/Domain/AuthToken.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Security/Common/Domain/AuthToken.cs diff --git a/Security/Common/Domain/AuthToken.cs b/Security/Common/Domain/AuthToken.cs new file mode 100644 index 0000000..4572e62 --- /dev/null +++ b/Security/Common/Domain/AuthToken.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mirea.Api.Security.Common.Domain; + +public class AuthToken +{ + public required string RefreshToken { get; set; } + public required string UserAgent { get; set; } + public required string Ip { get; set; } + public required string UserId { get; set; } + public required string AccessToken { get; set; } + public DateTime CreatedAt { get; set; } +} \ No newline at end of file From a3a42dd5c2fdfcadbffa51e9dfef4ac037894654 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:35:44 +0300 Subject: [PATCH 065/126] feat: add generate refresh token --- Security/Services/AuthService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 97aca20..107b7e8 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -5,4 +5,7 @@ namespace Mirea.Api.Security.Services; public class AuthService() { public TimeSpan Lifetime { private get; init; } + + private static string GenerateRefreshToken() => Guid.NewGuid().ToString().Replace("-", "") + + GeneratorKey.GenerateString(32); } \ No newline at end of file From 4240ad8110d65c94fd908ef171749d0500a46e16 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:36:26 +0300 Subject: [PATCH 066/126] feat: add auth key for cache --- Security/Services/AuthService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 107b7e8..e13f565 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -8,4 +8,6 @@ public class AuthService() private static string GenerateRefreshToken() => Guid.NewGuid().ToString().Replace("-", "") + GeneratorKey.GenerateString(32); + + private static string GetAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token"; } \ No newline at end of file From 43011457d678e79212467c3923a146e873d9e534 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:50:47 +0300 Subject: [PATCH 067/126] feat: add wrap for save to cache --- Security/Services/AuthService.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index e13f565..90ceb14 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -1,8 +1,12 @@ -using System; +using Mirea.Api.Security.Common.Domain; +using Mirea.Api.Security.Common.Dto.Requests; +using Mirea.Api.Security.Common.Dto.Responses; +using Mirea.Api.Security.Common.Interfaces; +using System; namespace Mirea.Api.Security.Services; -public class AuthService() +public class AuthService(ICacheService cache) { public TimeSpan Lifetime { private get; init; } @@ -10,4 +14,11 @@ public class AuthService() GeneratorKey.GenerateString(32); private static string GetAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token"; + + private Task SetAuthTokenDataToCache(string fingerprint, AuthToken data, CancellationToken cancellation) => + cache.SetAsync( + GetAuthCacheKey(fingerprint), + JsonSerializer.SerializeToUtf8Bytes(data), + slidingExpiration: Lifetime, + cancellationToken: cancellation); } \ No newline at end of file From f3063c53221a27b4c093891706565227469faada Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:51:03 +0300 Subject: [PATCH 068/126] feat: add generate access token --- Security/Services/AuthService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 90ceb14..c0be65c 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -6,12 +6,14 @@ using System; namespace Mirea.Api.Security.Services; -public class AuthService(ICacheService cache) +public class AuthService(ICacheService cache, IAccessToken accessTokenService) { public TimeSpan Lifetime { private get; init; } private static string GenerateRefreshToken() => Guid.NewGuid().ToString().Replace("-", "") + GeneratorKey.GenerateString(32); + private (string Token, DateTime ExpireIn) GenerateAccessToken(string userId) => + accessTokenService.GenerateToken(userId); private static string GetAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token"; From 81f2f995b051fb881b076cc5d63789594458650e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:51:32 +0300 Subject: [PATCH 069/126] feat: add generate auth token --- Security/Services/AuthService.cs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index c0be65c..471ba37 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -3,6 +3,10 @@ using Mirea.Api.Security.Common.Dto.Requests; using Mirea.Api.Security.Common.Dto.Responses; using Mirea.Api.Security.Common.Interfaces; using System; +using System.Security; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; namespace Mirea.Api.Security.Services; @@ -23,4 +27,31 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService) JsonSerializer.SerializeToUtf8Bytes(data), slidingExpiration: Lifetime, cancellationToken: cancellation); + + public async Task GenerateAuthTokensAsync(TokenRequest request, string preAuthToken, CancellationToken cancellation = default) + { + string userId = await new PreAuthService(cache).MatchToken(request, preAuthToken, cancellation); + + var refreshToken = GenerateRefreshToken(); + var accessToken = GenerateAccessToken(userId); + + var authTokenStruct = new AuthToken + { + CreatedAt = DateTime.UtcNow, + Ip = request.Ip, + RefreshToken = refreshToken, + UserAgent = request.UserAgent, + UserId = userId, + AccessToken = accessToken.Token + }; + + await SetAuthTokenDataToCache(request.Fingerprint, authTokenStruct, cancellation); + + return new AuthTokenResponse + { + AccessToken = accessToken.Token, + ExpiresIn = accessToken.ExpireIn, + RefreshToken = authTokenStruct.RefreshToken + }; + } } \ No newline at end of file From 79fb05d428526a1b57918a3871c6ee769aaf1243 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:54:45 +0300 Subject: [PATCH 070/126] feat: add token revocation --- Security/Common/Interfaces/IRevokedToken.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Security/Common/Interfaces/IRevokedToken.cs diff --git a/Security/Common/Interfaces/IRevokedToken.cs b/Security/Common/Interfaces/IRevokedToken.cs new file mode 100644 index 0000000..d8d9edc --- /dev/null +++ b/Security/Common/Interfaces/IRevokedToken.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace Mirea.Api.Security.Common.Interfaces; + +public interface IRevokedToken +{ + Task AddTokenToRevokedAsync(string token, DateTimeOffset expiresIn); + Task IsTokenRevokedAsync(string token); +} \ No newline at end of file From 9dd505a608de358fdf08b520c3b770b21ce63b9e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:55:13 +0300 Subject: [PATCH 071/126] feat: add auth token response --- Security/Common/Dto/Responses/AuthTokenResponse.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Security/Common/Dto/Responses/AuthTokenResponse.cs diff --git a/Security/Common/Dto/Responses/AuthTokenResponse.cs b/Security/Common/Dto/Responses/AuthTokenResponse.cs new file mode 100644 index 0000000..0c8a3d4 --- /dev/null +++ b/Security/Common/Dto/Responses/AuthTokenResponse.cs @@ -0,0 +1,10 @@ +using System; + +namespace Mirea.Api.Security.Common.Dto.Responses; + +public class AuthTokenResponse +{ + public required string AccessToken { get; set; } + public required string RefreshToken { get; set; } + public DateTime ExpiresIn { get; set; } +} \ No newline at end of file From 4138c7000757f6b1740d4295f88e1f7998cf25be Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:55:31 +0300 Subject: [PATCH 072/126] feat: add wrap for revoke access token --- Security/Services/AuthService.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 471ba37..392e721 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Mirea.Api.Security.Services; -public class AuthService(ICacheService cache, IAccessToken accessTokenService) +public class AuthService(ICacheService cache, IAccessToken accessTokenService, IRevokedToken revokedToken) { public TimeSpan Lifetime { private get; init; } @@ -28,6 +28,9 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService) slidingExpiration: Lifetime, cancellationToken: cancellation); + private Task RevokeAccessToken(string token) => + revokedToken.AddTokenToRevokedAsync(token, accessTokenService.GetExpireDateTime(token)); + public async Task GenerateAuthTokensAsync(TokenRequest request, string preAuthToken, CancellationToken cancellation = default) { string userId = await new PreAuthService(cache).MatchToken(request, preAuthToken, cancellation); From d84011cd71d71be0eb6e7238f99dac7c12f5140e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:55:57 +0300 Subject: [PATCH 073/126] feat: add refresh token --- Security/Services/AuthService.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 392e721..9542249 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -57,4 +57,33 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I RefreshToken = authTokenStruct.RefreshToken }; } + + public async Task RefreshTokenAsync(TokenRequest request, string refreshToken, CancellationToken cancellation = default) + { + var authToken = await cache.GetAsync(GetAuthCacheKey(request.Fingerprint), cancellation) + ?? throw new SecurityException(request.Fingerprint); + + if (authToken.RefreshToken != refreshToken || + authToken.UserAgent != request.UserAgent && + authToken.Ip != request.Ip) + { + await cache.RemoveAsync(request.Fingerprint, cancellation); + await RevokeAccessToken(authToken.AccessToken); + + throw new SecurityException(request.Fingerprint); + } + + var accessToken = GenerateAccessToken(authToken.UserId); + await RevokeAccessToken(authToken.AccessToken); + + authToken.AccessToken = accessToken.Token; + await SetAuthTokenDataToCache(request.Fingerprint, authToken, cancellation); + + return new AuthTokenResponse + { + AccessToken = accessToken.Token, + ExpiresIn = accessToken.ExpireIn, + RefreshToken = GenerateRefreshToken() + }; + } } \ No newline at end of file From 61218c38a0acfd5d361861b18a158e289938162d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 05:56:27 +0300 Subject: [PATCH 074/126] feat: add logout --- Security/Services/AuthService.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 9542249..58d439b 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -86,4 +86,14 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I RefreshToken = GenerateRefreshToken() }; } + + public async Task LogoutAsync(string fingerprint, CancellationToken cancellation = default) + { + var authTokenStruct = await cache.GetAsync(GetAuthCacheKey(fingerprint), cancellation); + if (authTokenStruct == null) return; + + await RevokeAccessToken(authTokenStruct.AccessToken); + + await cache.RemoveAsync(fingerprint, cancellation); + } } \ No newline at end of file From 25b6c7d69148c04d9a37abc4250fdceb6a2bc31a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:00:15 +0300 Subject: [PATCH 075/126] feat: add method if there is no pre-auth token --- Security/Services/AuthService.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Security/Services/AuthService.cs b/Security/Services/AuthService.cs index 58d439b..5426532 100644 --- a/Security/Services/AuthService.cs +++ b/Security/Services/AuthService.cs @@ -31,10 +31,8 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I private Task RevokeAccessToken(string token) => revokedToken.AddTokenToRevokedAsync(token, accessTokenService.GetExpireDateTime(token)); - public async Task GenerateAuthTokensAsync(TokenRequest request, string preAuthToken, CancellationToken cancellation = default) + public async Task GenerateAuthTokensAsync(TokenRequest request, string userId, CancellationToken cancellation = default) { - string userId = await new PreAuthService(cache).MatchToken(request, preAuthToken, cancellation); - var refreshToken = GenerateRefreshToken(); var accessToken = GenerateAccessToken(userId); @@ -58,6 +56,12 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I }; } + public async Task GenerateAuthTokensWithPreAuthAsync(TokenRequest request, string preAuthToken, + CancellationToken cancellation = default) => + await GenerateAuthTokensAsync(request, + await new PreAuthService(cache).MatchToken(request, preAuthToken, cancellation), + cancellation); + public async Task RefreshTokenAsync(TokenRequest request, string refreshToken, CancellationToken cancellation = default) { var authToken = await cache.GetAsync(GetAuthCacheKey(request.Fingerprint), cancellation) From 2efdc6dbfe373914af7385c9d9a4a802636c9864 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:04:09 +0300 Subject: [PATCH 076/126] feat: add auth service to DI --- Security/DependencyInjection.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Security/DependencyInjection.cs b/Security/DependencyInjection.cs index 5441ebc..ed16c5e 100644 --- a/Security/DependencyInjection.cs +++ b/Security/DependencyInjection.cs @@ -26,7 +26,7 @@ public static class DependencyInjection Secret = configuration["SECURITY_HASH_TOKEN"] }); - var lifeTimeLogin = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_1_FA"]!)); + var lifeTimePreAuthToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_1_FA"]!)); services.AddSingleton(provider => { @@ -34,7 +34,21 @@ public static class DependencyInjection return new PreAuthService(cache) { - Lifetime = lifeTimeLogin + Lifetime = lifeTimePreAuthToken + }; + }); + + var lifeTimeRefreshToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_RT"]!)); + + services.AddSingleton(provider => + { + var cacheService = provider.GetRequiredService(); + var accessTokenService = provider.GetRequiredService(); + var revokedTokenService = provider.GetRequiredService(); + + return new AuthService(cacheService, accessTokenService, revokedTokenService) + { + Lifetime = lifeTimeRefreshToken }; }); From 9287acf7d269410ff1d981dfab29833cdad7e3ae Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:08:14 +0300 Subject: [PATCH 077/126] feat: add cache implementations depending on the type --- .../Security/DistributedCacheService.cs | 32 +++++++++++++++++ .../Services/Security/MemoryCacheService.cs | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 Endpoint/Common/Services/Security/DistributedCacheService.cs create mode 100644 Endpoint/Common/Services/Security/MemoryCacheService.cs diff --git a/Endpoint/Common/Services/Security/DistributedCacheService.cs b/Endpoint/Common/Services/Security/DistributedCacheService.cs new file mode 100644 index 0000000..fa4c116 --- /dev/null +++ b/Endpoint/Common/Services/Security/DistributedCacheService.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Caching.Distributed; +using Mirea.Api.Security.Common.Interfaces; +using System.Text.Json; +using System.Threading.Tasks; +using System.Threading; +using System; + +namespace Mirea.Api.Endpoint.Common.Services.Security; + +public class DistributedCacheService(IDistributedCache cache) : ICacheService +{ + public async Task SetAsync(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default) + { + var options = new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow, + SlidingExpiration = slidingExpiration + }; + + var serializedValue = JsonSerializer.SerializeToUtf8Bytes(value); + await cache.SetAsync(key, serializedValue, options, cancellationToken); + } + + public async Task GetAsync(string key, CancellationToken cancellationToken = default) + { + var cachedValue = await cache.GetAsync(key, cancellationToken); + return cachedValue == null ? default : JsonSerializer.Deserialize(cachedValue); + } + + public Task RemoveAsync(string key, CancellationToken cancellationToken = default) => + cache.RemoveAsync(key, cancellationToken); +} \ No newline at end of file diff --git a/Endpoint/Common/Services/Security/MemoryCacheService.cs b/Endpoint/Common/Services/Security/MemoryCacheService.cs new file mode 100644 index 0000000..08d0a7f --- /dev/null +++ b/Endpoint/Common/Services/Security/MemoryCacheService.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Caching.Memory; +using Mirea.Api.Security.Common.Interfaces; +using System.Threading.Tasks; +using System.Threading; +using System; + +namespace Mirea.Api.Endpoint.Common.Services.Security; + +public class MemoryCacheService(IMemoryCache cache) : ICacheService +{ + public Task SetAsync(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default) + { + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow, + SlidingExpiration = slidingExpiration + }; + + cache.Set(key, value, options); + return Task.CompletedTask; + } + + public Task GetAsync(string key, CancellationToken cancellationToken = default) + { + cache.TryGetValue(key, out T? value); + return Task.FromResult(value); + } + + public Task RemoveAsync(string key, CancellationToken cancellationToken = default) + { + cache.Remove(key); + return Task.CompletedTask; + } +} \ No newline at end of file From 526bf5682b17f17995f3d2b47a867c3aa2c821f8 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:08:41 +0300 Subject: [PATCH 078/126] build: add security ref --- Endpoint/Endpoint.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Endpoint/Endpoint.csproj b/Endpoint/Endpoint.csproj index a1e08aa..3d4a2f6 100644 --- a/Endpoint/Endpoint.csproj +++ b/Endpoint/Endpoint.csproj @@ -32,6 +32,7 @@ + \ No newline at end of file From 6f02021fe7b12173c7614e933d7be4b6ab580d8f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:11:18 +0300 Subject: [PATCH 079/126] feat: add revoked token service --- .../Security/MemoryRevokedTokenService.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Endpoint/Common/Services/Security/MemoryRevokedTokenService.cs diff --git a/Endpoint/Common/Services/Security/MemoryRevokedTokenService.cs b/Endpoint/Common/Services/Security/MemoryRevokedTokenService.cs new file mode 100644 index 0000000..94c2f75 --- /dev/null +++ b/Endpoint/Common/Services/Security/MemoryRevokedTokenService.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Caching.Memory; +using Mirea.Api.Security.Common.Interfaces; +using System; +using System.Threading.Tasks; + +namespace Mirea.Api.Endpoint.Common.Services.Security; + +public class MemoryRevokedTokenService(IMemoryCache cache) : IRevokedToken +{ + public Task AddTokenToRevokedAsync(string token, DateTimeOffset expiresIn) + { + cache.Set(token, true, expiresIn); + return Task.CompletedTask; + } + + public Task IsTokenRevokedAsync(string token) => Task.FromResult(cache.TryGetValue(token, out _)); +} \ No newline at end of file From 62a859b44c550627b0c92d899ac32c55b8a205a5 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:11:29 +0300 Subject: [PATCH 080/126] style: clean code --- .../Common/Services/Security/DistributedCacheService.cs | 6 +++--- Endpoint/Common/Services/Security/MemoryCacheService.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Endpoint/Common/Services/Security/DistributedCacheService.cs b/Endpoint/Common/Services/Security/DistributedCacheService.cs index fa4c116..bf3dc39 100644 --- a/Endpoint/Common/Services/Security/DistributedCacheService.cs +++ b/Endpoint/Common/Services/Security/DistributedCacheService.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.Caching.Distributed; using Mirea.Api.Security.Common.Interfaces; -using System.Text.Json; -using System.Threading.Tasks; -using System.Threading; using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Common.Services.Security; diff --git a/Endpoint/Common/Services/Security/MemoryCacheService.cs b/Endpoint/Common/Services/Security/MemoryCacheService.cs index 08d0a7f..a428034 100644 --- a/Endpoint/Common/Services/Security/MemoryCacheService.cs +++ b/Endpoint/Common/Services/Security/MemoryCacheService.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Caching.Memory; using Mirea.Api.Security.Common.Interfaces; -using System.Threading.Tasks; -using System.Threading; using System; +using System.Threading; +using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Common.Services.Security; From f2aa274d0af0b665beb17c32d8295fdd405af8bf Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:28:21 +0300 Subject: [PATCH 081/126] build: add jwt ref --- Endpoint/Endpoint.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Endpoint/Endpoint.csproj b/Endpoint/Endpoint.csproj index 3d4a2f6..985a730 100644 --- a/Endpoint/Endpoint.csproj +++ b/Endpoint/Endpoint.csproj @@ -23,9 +23,10 @@ - + + From 85802aa514a04677a1d9365de6d678a36dd246f5 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:28:42 +0300 Subject: [PATCH 082/126] feat: add jwt token service --- .../Services/Security/JwtTokenService.cs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Endpoint/Common/Services/Security/JwtTokenService.cs diff --git a/Endpoint/Common/Services/Security/JwtTokenService.cs b/Endpoint/Common/Services/Security/JwtTokenService.cs new file mode 100644 index 0000000..7c3225f --- /dev/null +++ b/Endpoint/Common/Services/Security/JwtTokenService.cs @@ -0,0 +1,82 @@ +using Microsoft.IdentityModel.Tokens; +using Mirea.Api.Security.Common.Interfaces; +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; + +namespace Mirea.Api.Endpoint.Common.Services.Security; + +public class JwtTokenService : IAccessToken +{ + public required string Issuer { private get; init; } + public required string Audience { private get; init; } + public TimeSpan Lifetime { private get; init; } + + public ReadOnlyMemory EncryptionKey { get; init; } + public ReadOnlyMemory SigningKey { private get; init; } + + public (string Token, DateTime ExpireIn) GenerateToken(string userId) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var signingKey = new SymmetricSecurityKey(SigningKey.ToArray()); + var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray()); + var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha512); + + var expires = DateTime.UtcNow.Add(Lifetime); + + var tokenDescriptor = new SecurityTokenDescriptor + { + Issuer = Issuer, + Audience = Audience, + Expires = expires, + SigningCredentials = signingCredentials, + Subject = new ClaimsIdentity( + [ + new Claim(ClaimTypes.Name, userId), + // todo: get role by userId + new Claim(ClaimTypes.Role, "") + ]), + EncryptingCredentials = new EncryptingCredentials(encryptionKey, SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512) + }; + + var token = tokenHandler.CreateToken(tokenDescriptor); + + return (tokenHandler.WriteToken(token), expires); + } + + public DateTimeOffset GetExpireDateTime(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var signingKey = new SymmetricSecurityKey(SigningKey.ToArray()); + var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray()); + + var tokenValidationParameters = new TokenValidationParameters + { + ValidIssuer = Issuer, + ValidAudience = Audience, + IssuerSigningKey = signingKey, + TokenDecryptionKey = encryptionKey, + ValidateIssuer = true, + ValidateAudience = true, + ValidateIssuerSigningKey = true, + ValidateLifetime = false + }; + + try + { + var claimsPrincipal = tokenHandler.ValidateToken(token, tokenValidationParameters, out _); + + var expClaim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "exp"); + + if (expClaim != null && long.TryParse(expClaim.Value, out var expUnix)) + return DateTimeOffset.FromUnixTimeSeconds(expUnix); + } + catch (SecurityTokenException) + { + return DateTimeOffset.MinValue; + } + + return DateTimeOffset.MinValue; + } +} \ No newline at end of file From 38ec80a566fc55aef362a988d9335b61524ba9bf Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:30:01 +0300 Subject: [PATCH 083/126] feat: add configuration for jwt token --- Endpoint/Program.cs | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 89d98ab..adeeb99 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; @@ -6,20 +7,24 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; +using Mirea.Api.Endpoint.Common.Services.Security; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Validators; using Mirea.Api.Endpoint.Configuration.Swagger; using Mirea.Api.Endpoint.Middleware; +using Mirea.Api.Security.Common.Interfaces; using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections; using System.IO; using System.Linq; +using System.Text; namespace Mirea.Api.Endpoint; @@ -40,6 +45,58 @@ public class Program return result.Build(); } + private static IServiceCollection ConfigureJwtToken(IServiceCollection services, IConfiguration configuration) + { + var lifeTimeJwt = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_JWT"]!)); + + var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty); + + if (jwtDecrypt.Length != 32) + throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length); + + var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty); + + if (jwtKey.Length != 64) + throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length); + + var jwtIssuer = configuration["SECURITY_JWT_ISSUER"]; + var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"]; + + if (string.IsNullOrEmpty(jwtAudience) || string.IsNullOrEmpty(jwtIssuer)) + throw new InvalidOperationException("The \"SECURITY_JWT_ISSUER\" and \"SECURITY_JWT_AUDIENCE\" are not specified"); + + services.AddSingleton(_ => new JwtTokenService + { + Audience = jwtAudience, + Issuer = jwtIssuer, + Lifetime = lifeTimeJwt, + EncryptionKey = jwtDecrypt, + SigningKey = jwtKey + }); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = jwtIssuer, + + ValidateAudience = true, + ValidAudience = jwtAudience, + + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(jwtKey), + TokenDecryptionKey = new SymmetricSecurityKey(jwtDecrypt) + }; + }); + + return services; + } public static void Main(string[] args) { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); From d2ef99d0b29cbfc3036b4029b52ffafdb4e82011 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 06:42:14 +0300 Subject: [PATCH 084/126] feat: add security configure --- Endpoint/Program.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index adeeb99..840504f 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -97,6 +97,14 @@ public class Program return services; } + + private static IServiceCollection ConfigureSecurity(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + + return services; + } public static void Main(string[] args) { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); From 081c814036f86cf8714d93159ca18eec3054e225 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 07:38:32 +0300 Subject: [PATCH 085/126] feat: return the schedule-related settings --- Endpoint/Controllers/V1/ScheduleController.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Endpoint/Controllers/V1/ScheduleController.cs b/Endpoint/Controllers/V1/ScheduleController.cs index a0d5b7b..904e3d2 100644 --- a/Endpoint/Controllers/V1/ScheduleController.cs +++ b/Endpoint/Controllers/V1/ScheduleController.cs @@ -1,11 +1,16 @@ using MediatR; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Mirea.Api.DataAccess.Application.Cqrs.Schedule.Queries.GetScheduleList; +using Mirea.Api.Dto.Common; using Mirea.Api.Dto.Requests; using Mirea.Api.Dto.Responses; using Mirea.Api.Dto.Responses.Schedule; using Mirea.Api.Endpoint.Common.Attributes; +using Mirea.Api.Endpoint.Common.Services; +using Mirea.Api.Endpoint.Configuration.General; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -13,8 +18,14 @@ using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Controllers.V1; [ApiVersion("1.0")] -public class ScheduleController(IMediator mediator) : BaseController +public class ScheduleController(IMediator mediator, IOptionsSnapshot config) : BaseController { + [HttpGet("StartTerm")] + public ActionResult GetStartTerm() => config.Value.ScheduleSettings!.StartTerm; + + [HttpGet("PairPeriod")] + public ActionResult> GetPairPeriod() => config.Value.ScheduleSettings!.PairPeriod.ConvertToDto(); + /// /// Retrieves schedules based on various filters. /// From bf3a9d4b36ef4a8cb1a87a83c992d50510b6475a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 07:49:42 +0300 Subject: [PATCH 086/126] refactor: move database-related projects to separate folder --- .../Common/Mappings/AssemblyMappingProfile.cs | 28 ------------------- Application/Common/Mappings/IMapWith.cs | 9 ------ .../Application}/Application.csproj | 11 ++++---- .../Common/Behaviors/ValidationBehavior.cs | 0 .../Common/Exceptions/NotFoundException.cs | 0 .../CampusBasicInfoDto.cs | 0 .../CampusBasicInfoVm.cs | 0 .../GetCampusBasicInfoListQuery.cs | 0 .../GetCampusBasicInfoListQueryHandler.cs | 0 .../GetCampusDetails/CampusDetailsVm.cs | 0 .../GetCampusDetails/GetCampusDetailsQuery.cs | 0 .../GetCampusDetailsQueryHandler.cs | 0 .../GetDisciplineDetails/DisciplineInfoVm.cs | 0 .../GetDisciplineInfoQuery.cs | 0 .../GetDisciplineInfoQueryHandler.cs | 0 .../GetDisciplineList/DisciplineListVm.cs | 0 .../GetDisciplineList/DisciplineLookupDto.cs | 0 .../GetDisciplineListQuery.cs | 0 .../GetDisciplineListQueryHandler.cs | 0 .../GetFacultyDetails/FacultyInfoVm.cs | 0 .../GetFacultyDetails/GetFacultyInfoQuery.cs | 0 .../GetFacultyInfoQueryHandler.cs | 0 .../Queries/GetFacultyList/FacultyListVm.cs | 0 .../GetFacultyList/FacultyLookupDto.cs | 0 .../GetFacultyList/GetFacultyListQuery.cs | 0 .../GetFacultyListQueryHandler.cs | 0 .../GetGroupDetails/GetGroupInfoQuery.cs | 0 .../GetGroupInfoQueryHandler.cs | 0 .../Queries/GetGroupDetails/GroupInfoVm.cs | 0 .../Queries/GetGroupList/GetGroupListQuery.cs | 0 .../GetGroupList/GetGroupListQueryHandler.cs | 0 .../Group/Queries/GetGroupList/GroupListVm.cs | 0 .../Queries/GetGroupList/GroupLookupDto.cs | 0 .../GetLectureHallInfoQuery.cs | 0 .../GetLectureHallInfoQueryHandler.cs | 0 .../LectureHallInfoVm.cs | 0 .../GetLectureHallListQuery.cs | 0 .../GetLectureHallListQueryHandler.cs | 0 .../GetLectureHallList/LectureHallListVm.cs | 0 .../LectureHallLookupDto.cs | 0 .../GetProfessorInfoQuery.cs | 0 .../GetProfessorInfoQueryHandler.cs | 0 .../GetProfessorDetails/ProfessorInfoVm.cs | 0 .../GetProfessorList/GetProfessorListQuery.cs | 0 .../GetProfessorListQueryHandler.cs | 0 .../GetProfessorList/ProfessorListVm.cs | 0 .../GetProfessorList/ProfessorLookupDto.cs | 0 .../GetScheduleList/GetScheduleListQuery.cs | 0 .../GetScheduleListQueryHandler.cs | 0 .../Queries/GetScheduleList/ScheduleListVm.cs | 0 .../GetScheduleList/ScheduleLookupDto.cs | 0 .../Application}/DependencyInjection.cs | 0 .../Interfaces/DbContexts/IDbContextBase.cs | 0 .../DbContexts/Schedule/ICampusDbContext.cs | 0 .../Schedule/IDisciplineDbContext.cs | 0 .../DbContexts/Schedule/IFacultyDbContext.cs | 0 .../DbContexts/Schedule/IGroupDbContext.cs | 0 .../Schedule/ILectureHallDbContext.cs | 0 .../Schedule/ILessonAssociationDbContext.cs | 0 .../DbContexts/Schedule/ILessonDbContext.cs | 0 .../Schedule/IProfessorDbContext.cs | 0 .../Schedule/ISpecificWeekDbContext.cs | 0 .../Schedule/ITypeOfOccupationDbContext.cs | 0 {Domain => SqlData/Domain}/Domain.csproj | 0 {Domain => SqlData/Domain}/Schedule/Campus.cs | 0 .../Domain}/Schedule/Discipline.cs | 0 .../Domain}/Schedule/Faculty.cs | 0 {Domain => SqlData/Domain}/Schedule/Group.cs | 0 .../Domain}/Schedule/LectureHall.cs | 0 {Domain => SqlData/Domain}/Schedule/Lesson.cs | 0 .../Domain}/Schedule/LessonAssociation.cs | 0 .../Domain}/Schedule/Professor.cs | 0 .../Domain}/Schedule/SpecificWeek.cs | 0 .../Domain}/Schedule/TypeOfOccupation.cs | 0 74 files changed, 5 insertions(+), 43 deletions(-) delete mode 100644 Application/Common/Mappings/AssemblyMappingProfile.cs delete mode 100644 Application/Common/Mappings/IMapWith.cs rename {Application => SqlData/Application}/Application.csproj (53%) rename {Application => SqlData/Application}/Common/Behaviors/ValidationBehavior.cs (100%) rename {Application => SqlData/Application}/Common/Exceptions/NotFoundException.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusDetails/CampusDetailsVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineDetails/DisciplineInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineLookupDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyDetails/FacultyInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyList/FacultyListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyList/FacultyLookupDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupDetails/GroupInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupList/GetGroupListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupList/GetGroupListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupList/GroupListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Group/Queries/GetGroupList/GroupLookupDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallDetails/LectureHallInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallLookupDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorDetails/ProfessorInfoVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorList/ProfessorListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Professor/Queries/GetProfessorList/ProfessorLookupDto.cs (100%) rename {Application => SqlData/Application}/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQuery.cs (100%) rename {Application => SqlData/Application}/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQueryHandler.cs (100%) rename {Application => SqlData/Application}/Cqrs/Schedule/Queries/GetScheduleList/ScheduleListVm.cs (100%) rename {Application => SqlData/Application}/Cqrs/Schedule/Queries/GetScheduleList/ScheduleLookupDto.cs (100%) rename {Application => SqlData/Application}/DependencyInjection.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/IDbContextBase.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ICampusDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/IDisciplineDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/IFacultyDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/IGroupDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ILectureHallDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ILessonAssociationDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ILessonDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/IProfessorDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ISpecificWeekDbContext.cs (100%) rename {Application => SqlData/Application}/Interfaces/DbContexts/Schedule/ITypeOfOccupationDbContext.cs (100%) rename {Domain => SqlData/Domain}/Domain.csproj (100%) rename {Domain => SqlData/Domain}/Schedule/Campus.cs (100%) rename {Domain => SqlData/Domain}/Schedule/Discipline.cs (100%) rename {Domain => SqlData/Domain}/Schedule/Faculty.cs (100%) rename {Domain => SqlData/Domain}/Schedule/Group.cs (100%) rename {Domain => SqlData/Domain}/Schedule/LectureHall.cs (100%) rename {Domain => SqlData/Domain}/Schedule/Lesson.cs (100%) rename {Domain => SqlData/Domain}/Schedule/LessonAssociation.cs (100%) rename {Domain => SqlData/Domain}/Schedule/Professor.cs (100%) rename {Domain => SqlData/Domain}/Schedule/SpecificWeek.cs (100%) rename {Domain => SqlData/Domain}/Schedule/TypeOfOccupation.cs (100%) diff --git a/Application/Common/Mappings/AssemblyMappingProfile.cs b/Application/Common/Mappings/AssemblyMappingProfile.cs deleted file mode 100644 index 68a9e8e..0000000 --- a/Application/Common/Mappings/AssemblyMappingProfile.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AutoMapper; -using System; -using System.Linq; -using System.Reflection; - -namespace Mirea.Api.DataAccess.Application.Common.Mappings; - -public class AssemblyMappingProfile : Profile -{ - public AssemblyMappingProfile(Assembly assembly) => - ApplyMappingsFromAssembly(assembly); - - private void ApplyMappingsFromAssembly(Assembly assembly) - { - var types = assembly.GetExportedTypes() - .Where(type => type.GetInterfaces() - .Any(i => i.IsGenericType && - i.GetGenericTypeDefinition() == typeof(IMapWith<>))) - .ToList(); - - foreach (var type in types) - { - var instance = Activator.CreateInstance(type); - var methodInfo = type.GetMethod("Mapping"); - methodInfo?.Invoke(instance, new[] { this }); - } - } -} \ No newline at end of file diff --git a/Application/Common/Mappings/IMapWith.cs b/Application/Common/Mappings/IMapWith.cs deleted file mode 100644 index 390e4e0..0000000 --- a/Application/Common/Mappings/IMapWith.cs +++ /dev/null @@ -1,9 +0,0 @@ -using AutoMapper; - -namespace Mirea.Api.DataAccess.Application.Common.Mappings; - -public interface IMapWith -{ - void Mapping(Profile profile) => - profile.CreateMap(typeof(T), GetType()); -} \ No newline at end of file diff --git a/Application/Application.csproj b/SqlData/Application/Application.csproj similarity index 53% rename from Application/Application.csproj rename to SqlData/Application/Application.csproj index 2003517..51f04ad 100644 --- a/Application/Application.csproj +++ b/SqlData/Application/Application.csproj @@ -13,15 +13,14 @@ - - - - - + + + + - + \ No newline at end of file diff --git a/Application/Common/Behaviors/ValidationBehavior.cs b/SqlData/Application/Common/Behaviors/ValidationBehavior.cs similarity index 100% rename from Application/Common/Behaviors/ValidationBehavior.cs rename to SqlData/Application/Common/Behaviors/ValidationBehavior.cs diff --git a/Application/Common/Exceptions/NotFoundException.cs b/SqlData/Application/Common/Exceptions/NotFoundException.cs similarity index 100% rename from Application/Common/Exceptions/NotFoundException.cs rename to SqlData/Application/Common/Exceptions/NotFoundException.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoDto.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoDto.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoDto.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoDto.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoVm.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoVm.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoVm.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/CampusBasicInfoVm.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQuery.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQuery.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQuery.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQuery.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQueryHandler.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQueryHandler.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusBasicInfoList/GetCampusBasicInfoListQueryHandler.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusDetails/CampusDetailsVm.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/CampusDetailsVm.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusDetails/CampusDetailsVm.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/CampusDetailsVm.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQuery.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQuery.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQuery.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQuery.cs diff --git a/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQueryHandler.cs b/SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQueryHandler.cs similarity index 100% rename from Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQueryHandler.cs rename to SqlData/Application/Cqrs/Campus/Queries/GetCampusDetails/GetCampusDetailsQueryHandler.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/DisciplineInfoVm.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/DisciplineInfoVm.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineDetails/DisciplineInfoVm.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/DisciplineInfoVm.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQuery.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQuery.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQuery.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQuery.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQueryHandler.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQueryHandler.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQueryHandler.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineDetails/GetDisciplineInfoQueryHandler.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineListVm.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineListVm.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineListVm.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineListVm.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineLookupDto.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineLookupDto.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineLookupDto.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/DisciplineLookupDto.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQuery.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQuery.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQuery.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQuery.cs diff --git a/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQueryHandler.cs b/SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQueryHandler.cs rename to SqlData/Application/Cqrs/Discipline/Queries/GetDisciplineList/GetDisciplineListQueryHandler.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyDetails/FacultyInfoVm.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/FacultyInfoVm.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyDetails/FacultyInfoVm.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/FacultyInfoVm.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQuery.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQuery.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQuery.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQuery.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQueryHandler.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQueryHandler.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQueryHandler.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyDetails/GetFacultyInfoQueryHandler.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyListVm.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyListVm.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyListVm.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyListVm.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyLookupDto.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyLookupDto.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyLookupDto.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/FacultyLookupDto.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQuery.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQuery.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQuery.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQuery.cs diff --git a/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQueryHandler.cs b/SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQueryHandler.cs rename to SqlData/Application/Cqrs/Faculty/Queries/GetFacultyList/GetFacultyListQueryHandler.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQuery.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQuery.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQuery.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQuery.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQueryHandler.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQueryHandler.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQueryHandler.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GetGroupInfoQueryHandler.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupDetails/GroupInfoVm.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GroupInfoVm.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupDetails/GroupInfoVm.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupDetails/GroupInfoVm.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQuery.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQuery.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQuery.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQuery.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQueryHandler.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQueryHandler.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupList/GetGroupListQueryHandler.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupList/GroupListVm.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupList/GroupListVm.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupList/GroupListVm.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupList/GroupListVm.cs diff --git a/Application/Cqrs/Group/Queries/GetGroupList/GroupLookupDto.cs b/SqlData/Application/Cqrs/Group/Queries/GetGroupList/GroupLookupDto.cs similarity index 100% rename from Application/Cqrs/Group/Queries/GetGroupList/GroupLookupDto.cs rename to SqlData/Application/Cqrs/Group/Queries/GetGroupList/GroupLookupDto.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQuery.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQuery.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQuery.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQuery.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQueryHandler.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQueryHandler.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQueryHandler.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/GetLectureHallInfoQueryHandler.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/LectureHallInfoVm.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/LectureHallInfoVm.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/LectureHallInfoVm.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallDetails/LectureHallInfoVm.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQuery.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQuery.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQuery.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQuery.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQueryHandler.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQueryHandler.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQueryHandler.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/GetLectureHallListQueryHandler.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallListVm.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallListVm.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallListVm.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallListVm.cs diff --git a/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallLookupDto.cs b/SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallLookupDto.cs similarity index 100% rename from Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallLookupDto.cs rename to SqlData/Application/Cqrs/LectureHall/Queries/GetLectureHallList/LectureHallLookupDto.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQuery.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQuery.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQuery.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQuery.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQueryHandler.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQueryHandler.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQueryHandler.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/GetProfessorInfoQueryHandler.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorDetails/ProfessorInfoVm.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/ProfessorInfoVm.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorDetails/ProfessorInfoVm.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorDetails/ProfessorInfoVm.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQuery.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQuery.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQuery.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQuery.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQueryHandler.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQueryHandler.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/GetProfessorListQueryHandler.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorListVm.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorListVm.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorListVm.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorListVm.cs diff --git a/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorLookupDto.cs b/SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorLookupDto.cs similarity index 100% rename from Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorLookupDto.cs rename to SqlData/Application/Cqrs/Professor/Queries/GetProfessorList/ProfessorLookupDto.cs diff --git a/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQuery.cs b/SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQuery.cs similarity index 100% rename from Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQuery.cs rename to SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQuery.cs diff --git a/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQueryHandler.cs b/SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQueryHandler.cs similarity index 100% rename from Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQueryHandler.cs rename to SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/GetScheduleListQueryHandler.cs diff --git a/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleListVm.cs b/SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleListVm.cs similarity index 100% rename from Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleListVm.cs rename to SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleListVm.cs diff --git a/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleLookupDto.cs b/SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleLookupDto.cs similarity index 100% rename from Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleLookupDto.cs rename to SqlData/Application/Cqrs/Schedule/Queries/GetScheduleList/ScheduleLookupDto.cs diff --git a/Application/DependencyInjection.cs b/SqlData/Application/DependencyInjection.cs similarity index 100% rename from Application/DependencyInjection.cs rename to SqlData/Application/DependencyInjection.cs diff --git a/Application/Interfaces/DbContexts/IDbContextBase.cs b/SqlData/Application/Interfaces/DbContexts/IDbContextBase.cs similarity index 100% rename from Application/Interfaces/DbContexts/IDbContextBase.cs rename to SqlData/Application/Interfaces/DbContexts/IDbContextBase.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ICampusDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ICampusDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ICampusDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ICampusDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/IDisciplineDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/IDisciplineDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/IDisciplineDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/IDisciplineDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/IFacultyDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/IFacultyDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/IFacultyDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/IFacultyDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/IGroupDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/IGroupDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/IGroupDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/IGroupDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ILectureHallDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ILectureHallDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ILectureHallDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ILectureHallDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ILessonAssociationDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ILessonAssociationDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ILessonAssociationDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ILessonAssociationDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ILessonDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ILessonDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ILessonDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ILessonDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/IProfessorDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/IProfessorDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/IProfessorDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/IProfessorDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ISpecificWeekDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ISpecificWeekDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ISpecificWeekDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ISpecificWeekDbContext.cs diff --git a/Application/Interfaces/DbContexts/Schedule/ITypeOfOccupationDbContext.cs b/SqlData/Application/Interfaces/DbContexts/Schedule/ITypeOfOccupationDbContext.cs similarity index 100% rename from Application/Interfaces/DbContexts/Schedule/ITypeOfOccupationDbContext.cs rename to SqlData/Application/Interfaces/DbContexts/Schedule/ITypeOfOccupationDbContext.cs diff --git a/Domain/Domain.csproj b/SqlData/Domain/Domain.csproj similarity index 100% rename from Domain/Domain.csproj rename to SqlData/Domain/Domain.csproj diff --git a/Domain/Schedule/Campus.cs b/SqlData/Domain/Schedule/Campus.cs similarity index 100% rename from Domain/Schedule/Campus.cs rename to SqlData/Domain/Schedule/Campus.cs diff --git a/Domain/Schedule/Discipline.cs b/SqlData/Domain/Schedule/Discipline.cs similarity index 100% rename from Domain/Schedule/Discipline.cs rename to SqlData/Domain/Schedule/Discipline.cs diff --git a/Domain/Schedule/Faculty.cs b/SqlData/Domain/Schedule/Faculty.cs similarity index 100% rename from Domain/Schedule/Faculty.cs rename to SqlData/Domain/Schedule/Faculty.cs diff --git a/Domain/Schedule/Group.cs b/SqlData/Domain/Schedule/Group.cs similarity index 100% rename from Domain/Schedule/Group.cs rename to SqlData/Domain/Schedule/Group.cs diff --git a/Domain/Schedule/LectureHall.cs b/SqlData/Domain/Schedule/LectureHall.cs similarity index 100% rename from Domain/Schedule/LectureHall.cs rename to SqlData/Domain/Schedule/LectureHall.cs diff --git a/Domain/Schedule/Lesson.cs b/SqlData/Domain/Schedule/Lesson.cs similarity index 100% rename from Domain/Schedule/Lesson.cs rename to SqlData/Domain/Schedule/Lesson.cs diff --git a/Domain/Schedule/LessonAssociation.cs b/SqlData/Domain/Schedule/LessonAssociation.cs similarity index 100% rename from Domain/Schedule/LessonAssociation.cs rename to SqlData/Domain/Schedule/LessonAssociation.cs diff --git a/Domain/Schedule/Professor.cs b/SqlData/Domain/Schedule/Professor.cs similarity index 100% rename from Domain/Schedule/Professor.cs rename to SqlData/Domain/Schedule/Professor.cs diff --git a/Domain/Schedule/SpecificWeek.cs b/SqlData/Domain/Schedule/SpecificWeek.cs similarity index 100% rename from Domain/Schedule/SpecificWeek.cs rename to SqlData/Domain/Schedule/SpecificWeek.cs diff --git a/Domain/Schedule/TypeOfOccupation.cs b/SqlData/Domain/Schedule/TypeOfOccupation.cs similarity index 100% rename from Domain/Schedule/TypeOfOccupation.cs rename to SqlData/Domain/Schedule/TypeOfOccupation.cs From 164d575d9872460da9d47e9bfb824fa28e3ddc1e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 29 May 2024 08:08:58 +0300 Subject: [PATCH 087/126] refactor: move database-related projects to a separate folder --- .../Contexts/Schedule/CampusDbContext.cs | 0 .../Contexts/Schedule/DisciplineDbContext.cs | 0 .../Contexts/Schedule/FacultyDbContext.cs | 0 .../Persistence}/Contexts/Schedule/GroupDbContext.cs | 0 .../Contexts/Schedule/LectureHallDbContext.cs | 0 .../Contexts/Schedule/LessonAssociationDbContext.cs | 0 .../Contexts/Schedule/LessonDbContext.cs | 0 .../Contexts/Schedule/ProfessorDbContext.cs | 0 .../Contexts/Schedule/SpecificWeekDbContext.cs | 0 .../Contexts/Schedule/TypeOfOccupationDbContext.cs | 0 .../Persistence}/DbInitializer.cs | 0 .../Persistence}/DependencyInjection.cs | 0 .../Schedule/CampusConfiguration.cs | 0 .../Schedule/DisciplineConfiguration.cs | 0 .../Schedule/FacultyConfiguration.cs | 0 .../Schedule/GroupConfiguration.cs | 0 .../Schedule/LectureHallConfiguration.cs | 0 .../Schedule/LessonAssociationConfiguration.cs | 0 .../Schedule/LessonConfiguration.cs | 0 .../Schedule/ProfessorConfiguration.cs | 0 .../Schedule/SpecificWeekConfiguration.cs | 0 .../Schedule/TypeOfOccupationConfiguration.cs | 0 .../Persistence}/Persistence.csproj | 12 ++++-------- .../Persistence}/UberDbContext.cs | 0 24 files changed, 4 insertions(+), 8 deletions(-) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/CampusDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/DisciplineDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/FacultyDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/GroupDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/LectureHallDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/LessonAssociationDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/LessonDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/ProfessorDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/SpecificWeekDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/Contexts/Schedule/TypeOfOccupationDbContext.cs (100%) rename {Persistence => SqlData/Persistence}/DbInitializer.cs (100%) rename {Persistence => SqlData/Persistence}/DependencyInjection.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/CampusConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/GroupConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/LessonConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs (100%) rename {Persistence => SqlData/Persistence}/Persistence.csproj (85%) rename {Persistence => SqlData/Persistence}/UberDbContext.cs (100%) diff --git a/Persistence/Contexts/Schedule/CampusDbContext.cs b/SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/CampusDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs diff --git a/Persistence/Contexts/Schedule/DisciplineDbContext.cs b/SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/DisciplineDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs diff --git a/Persistence/Contexts/Schedule/FacultyDbContext.cs b/SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/FacultyDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs diff --git a/Persistence/Contexts/Schedule/GroupDbContext.cs b/SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/GroupDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs diff --git a/Persistence/Contexts/Schedule/LectureHallDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/LectureHallDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs diff --git a/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/LessonAssociationDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs diff --git a/Persistence/Contexts/Schedule/LessonDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/LessonDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs diff --git a/Persistence/Contexts/Schedule/ProfessorDbContext.cs b/SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/ProfessorDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs diff --git a/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs b/SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/SpecificWeekDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs diff --git a/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs b/SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs similarity index 100% rename from Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs rename to SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs diff --git a/Persistence/DbInitializer.cs b/SqlData/Persistence/DbInitializer.cs similarity index 100% rename from Persistence/DbInitializer.cs rename to SqlData/Persistence/DbInitializer.cs diff --git a/Persistence/DependencyInjection.cs b/SqlData/Persistence/DependencyInjection.cs similarity index 100% rename from Persistence/DependencyInjection.cs rename to SqlData/Persistence/DependencyInjection.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs diff --git a/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs similarity index 100% rename from Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs diff --git a/Persistence/Persistence.csproj b/SqlData/Persistence/Persistence.csproj similarity index 85% rename from Persistence/Persistence.csproj rename to SqlData/Persistence/Persistence.csproj index 37f0a7f..d191edd 100644 --- a/Persistence/Persistence.csproj +++ b/SqlData/Persistence/Persistence.csproj @@ -13,11 +13,11 @@ - - + + - - + + @@ -25,8 +25,4 @@ - - - - \ No newline at end of file diff --git a/Persistence/UberDbContext.cs b/SqlData/Persistence/UberDbContext.cs similarity index 100% rename from Persistence/UberDbContext.cs rename to SqlData/Persistence/UberDbContext.cs From 0a9c98cbf9321dc5ef4cb6f84450d797aa5a86ed Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:07:20 +0300 Subject: [PATCH 088/126] refactor: change GetService to GetRequiredService --- SqlData/Persistence/DependencyInjection.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/SqlData/Persistence/DependencyInjection.cs b/SqlData/Persistence/DependencyInjection.cs index f98052c..2ebae49 100644 --- a/SqlData/Persistence/DependencyInjection.cs +++ b/SqlData/Persistence/DependencyInjection.cs @@ -22,16 +22,16 @@ public static class DependencyInjection services.AddDbContext(DbConfig); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); - services.AddScoped(provider => provider.GetService()!); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); return services; From 78a242f4c3fbd2da56c42ff649b68d2bd5aec238 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:07:59 +0300 Subject: [PATCH 089/126] feat: add providers database for presistence --- SqlData/Persistence/Common/DatabaseProvider.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 SqlData/Persistence/Common/DatabaseProvider.cs diff --git a/SqlData/Persistence/Common/DatabaseProvider.cs b/SqlData/Persistence/Common/DatabaseProvider.cs new file mode 100644 index 0000000..c3d15cf --- /dev/null +++ b/SqlData/Persistence/Common/DatabaseProvider.cs @@ -0,0 +1,8 @@ +namespace Mirea.Api.DataAccess.Persistence.Common; + +public enum DatabaseProvider +{ + Mysql, + Sqlite, + Postgresql +} \ No newline at end of file From 53a0439edb6b4653ee3f143fdce5473beb47bed3 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:10:13 +0300 Subject: [PATCH 090/126] feat: add wrap DbContext for OnModelCreating --- SqlData/Persistence/Common/BaseDbContext.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 SqlData/Persistence/Common/BaseDbContext.cs diff --git a/SqlData/Persistence/Common/BaseDbContext.cs b/SqlData/Persistence/Common/BaseDbContext.cs new file mode 100644 index 0000000..c3dd911 --- /dev/null +++ b/SqlData/Persistence/Common/BaseDbContext.cs @@ -0,0 +1,9 @@ +using Microsoft.EntityFrameworkCore; + +namespace Mirea.Api.DataAccess.Persistence.Common; + +public abstract class BaseDbContext(DbContextOptions options) : DbContext(options) where TContext : DbContext +{ + public void ApplyConfigurations(ModelBuilder modelBuilder) => + base.OnModelCreating(modelBuilder); +} \ No newline at end of file From f79c7c7db9e6693aa78858a7ad89c1a72c9cb273 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:11:18 +0300 Subject: [PATCH 091/126] refactor: use wrap DbContext --- SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs | 7 ++++--- .../Persistence/Contexts/Schedule/DisciplineDbContext.cs | 7 ++++--- SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs | 7 ++++--- SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs | 7 ++++--- .../Persistence/Contexts/Schedule/LectureHallDbContext.cs | 7 ++++--- .../Contexts/Schedule/LessonAssociationDbContext.cs | 7 ++++--- SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs | 7 ++++--- .../Persistence/Contexts/Schedule/ProfessorDbContext.cs | 7 ++++--- .../Persistence/Contexts/Schedule/SpecificWeekDbContext.cs | 7 ++++--- .../Contexts/Schedule/TypeOfOccupationDbContext.cs | 7 ++++--- 10 files changed, 40 insertions(+), 30 deletions(-) diff --git a/SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs b/SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs index 30a9d56..d93e3d9 100644 --- a/SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/CampusDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class CampusDbContext(DbContextOptions options) : DbContext(options), ICampusDbContext +public class CampusDbContext(DbContextOptions options) : BaseDbContext(options), ICampusDbContext { public DbSet Campuses { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new CampusConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs b/SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs index d0a5612..d9ec9e1 100644 --- a/SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/DisciplineDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class DisciplineDbContext(DbContextOptions options) : DbContext(options), IDisciplineDbContext +public sealed class DisciplineDbContext(DbContextOptions options) : BaseDbContext(options), IDisciplineDbContext { public DbSet Disciplines { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new DisciplineConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs b/SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs index ab6a45c..98323a6 100644 --- a/SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/FacultyDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class FacultyDbContext(DbContextOptions options) : DbContext(options), IFacultyDbContext +public sealed class FacultyDbContext(DbContextOptions options) : BaseDbContext(options), IFacultyDbContext { public DbSet Faculties { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new FacultyConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs b/SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs index 5d61c70..e6bcc5e 100644 --- a/SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/GroupDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class GroupDbContext(DbContextOptions options) : DbContext(options), IGroupDbContext +public sealed class GroupDbContext(DbContextOptions options) : BaseDbContext(options), IGroupDbContext { public DbSet Groups { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new GroupConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs index 67841a1..4253309 100644 --- a/SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/LectureHallDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class LectureHallDbContext(DbContextOptions options) : DbContext(options), ILectureHallDbContext +public sealed class LectureHallDbContext(DbContextOptions options) : BaseDbContext(options), ILectureHallDbContext { public DbSet LectureHalls { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new LectureHallConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs index 773bc8a..ae6be39 100644 --- a/SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/LessonAssociationDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class LessonAssociationDbContext(DbContextOptions options) : DbContext(options), ILessonAssociationDbContext +public sealed class LessonAssociationDbContext(DbContextOptions options) : BaseDbContext(options), ILessonAssociationDbContext { public DbSet LessonAssociations { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new LessonAssociationConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs b/SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs index d2dfcae..e727041 100644 --- a/SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/LessonDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class LessonDbContext(DbContextOptions options) : DbContext(options), ILessonDbContext +public sealed class LessonDbContext(DbContextOptions options) : BaseDbContext(options), ILessonDbContext { public DbSet Lessons { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new LessonConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs b/SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs index fcd51c6..75924ff 100644 --- a/SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/ProfessorDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class ProfessorDbContext(DbContextOptions options) : DbContext(options), IProfessorDbContext +public sealed class ProfessorDbContext(DbContextOptions options) : BaseDbContext(options), IProfessorDbContext { public DbSet Professors { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new ProfessorConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs b/SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs index b3601bd..20bbab7 100644 --- a/SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/SpecificWeekDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class SpecificWeekDbContext(DbContextOptions options) : DbContext(options), ISpecificWeekDbContext +public sealed class SpecificWeekDbContext(DbContextOptions options) : BaseDbContext(options), ISpecificWeekDbContext { public DbSet SpecificWeeks { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new SpecificWeekConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs b/SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs index b47ac61..ae49d35 100644 --- a/SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs +++ b/SqlData/Persistence/Contexts/Schedule/TypeOfOccupationDbContext.cs @@ -1,17 +1,18 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence.Contexts.Schedule; -public class TypeOfOccupationDbContext(DbContextOptions options) : DbContext(options), ITypeOfOccupationDbContext +public sealed class TypeOfOccupationDbContext(DbContextOptions options) : BaseDbContext(options), ITypeOfOccupationDbContext { public DbSet TypeOfOccupations { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new TypeOfOccupationConfiguration()); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file From 43b6ab79343aec823eb69622010f7c0a04929416 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:12:04 +0300 Subject: [PATCH 092/126] feat: add sealed class for Mark configuration namespace --- SqlData/Persistence/EntityTypeConfigurations/Mark.cs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mark.cs diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mark.cs b/SqlData/Persistence/EntityTypeConfigurations/Mark.cs new file mode 100644 index 0000000..734dd03 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mark.cs @@ -0,0 +1,3 @@ +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations; + +public sealed class Mark; \ No newline at end of file From 31c36443e17335461f951f6faf1d157c9529027f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:12:36 +0300 Subject: [PATCH 093/126] refactor: use wrap DbContext for UberDbContext --- SqlData/Persistence/UberDbContext.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/SqlData/Persistence/UberDbContext.cs b/SqlData/Persistence/UberDbContext.cs index ccd1877..3777ef2 100644 --- a/SqlData/Persistence/UberDbContext.cs +++ b/SqlData/Persistence/UberDbContext.cs @@ -1,10 +1,11 @@ using Microsoft.EntityFrameworkCore; using Mirea.Api.DataAccess.Domain.Schedule; -using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence; -public class UberDbContext(DbContextOptions options) : DbContext(options) +public class UberDbContext(DbContextOptions options) : BaseDbContext(options) { public DbSet Campuses { get; set; } = null!; public DbSet Disciplines { get; set; } = null!; @@ -19,17 +20,7 @@ public class UberDbContext(DbContextOptions options) : DbContext( protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.ApplyConfiguration(new CampusConfiguration()); - modelBuilder.ApplyConfiguration(new DisciplineConfiguration()); - modelBuilder.ApplyConfiguration(new FacultyConfiguration()); - modelBuilder.ApplyConfiguration(new GroupConfiguration()); - modelBuilder.ApplyConfiguration(new LectureHallConfiguration()); - modelBuilder.ApplyConfiguration(new LessonConfiguration()); - modelBuilder.ApplyConfiguration(new ProfessorConfiguration()); - modelBuilder.ApplyConfiguration(new LessonAssociationConfiguration()); - modelBuilder.ApplyConfiguration(new TypeOfOccupationConfiguration()); - modelBuilder.ApplyConfiguration(new SpecificWeekConfiguration()); - + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } } \ No newline at end of file From 1bdf40f31fc74dc1b9835a0077c7eae34c67afd0 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:13:06 +0300 Subject: [PATCH 094/126] build: move projects --- Backend.sln | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/Backend.sln b/Backend.sln index 5790acf..f24d8d8 100644 --- a/Backend.sln +++ b/Backend.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "Domain\Domain.csproj", "{C27FB5CD-6A70-4FB2-847A-847B34806902}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Endpoint", "Endpoint\Endpoint.csproj", "{F3A1D12E-F5B2-4339-9966-DBF869E78357}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elements of the solution", "Elements of the solution", "{3E087889-A4A0-4A55-A07D-7D149A5BC928}" @@ -19,13 +17,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elements of the solution", .gitea\workflows\test.yaml = .gitea\workflows\test.yaml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "Application\Application.csproj", "{E7F0A4D4-B032-4BB9-9526-1AF688F341A4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "Persistence\Persistence.csproj", "{4C1E558F-633F-438E-AC3A-61CDDED917C5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiDto", "ApiDto\ApiDto.csproj", "{0335FA36-E137-453F-853B-916674C168FE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Security", "Security\Security.csproj", "{47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Security", "Security\Security.csproj", "{47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SqlData", "SqlData", "{7E7A63CD-547B-4FB4-A383-EB75298020A1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "SqlData\Domain\Domain.csproj", "{3BFD6180-7CA7-4E85-A379-225B872439A1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "SqlData\Application\Application.csproj", "{0B1F3656-E5B3-440C-961F-A7D004FBE9A8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "SqlData\Persistence\Persistence.csproj", "{48C9998C-ECE2-407F-835F-1A7255A5C99E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,22 +35,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C27FB5CD-6A70-4FB2-847A-847B34806902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C27FB5CD-6A70-4FB2-847A-847B34806902}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C27FB5CD-6A70-4FB2-847A-847B34806902}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C27FB5CD-6A70-4FB2-847A-847B34806902}.Release|Any CPU.Build.0 = Release|Any CPU {F3A1D12E-F5B2-4339-9966-DBF869E78357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3A1D12E-F5B2-4339-9966-DBF869E78357}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3A1D12E-F5B2-4339-9966-DBF869E78357}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3A1D12E-F5B2-4339-9966-DBF869E78357}.Release|Any CPU.Build.0 = Release|Any CPU - {E7F0A4D4-B032-4BB9-9526-1AF688F341A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7F0A4D4-B032-4BB9-9526-1AF688F341A4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7F0A4D4-B032-4BB9-9526-1AF688F341A4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7F0A4D4-B032-4BB9-9526-1AF688F341A4}.Release|Any CPU.Build.0 = Release|Any CPU - {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C1E558F-633F-438E-AC3A-61CDDED917C5}.Release|Any CPU.Build.0 = Release|Any CPU {0335FA36-E137-453F-853B-916674C168FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0335FA36-E137-453F-853B-916674C168FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {0335FA36-E137-453F-853B-916674C168FE}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -57,10 +47,27 @@ Global {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {47A3C065-4E1D-4B1E-AAB4-2BB8F40E56B4}.Release|Any CPU.Build.0 = Release|Any CPU + {3BFD6180-7CA7-4E85-A379-225B872439A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BFD6180-7CA7-4E85-A379-225B872439A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BFD6180-7CA7-4E85-A379-225B872439A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BFD6180-7CA7-4E85-A379-225B872439A1}.Release|Any CPU.Build.0 = Release|Any CPU + {0B1F3656-E5B3-440C-961F-A7D004FBE9A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B1F3656-E5B3-440C-961F-A7D004FBE9A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B1F3656-E5B3-440C-961F-A7D004FBE9A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B1F3656-E5B3-440C-961F-A7D004FBE9A8}.Release|Any CPU.Build.0 = Release|Any CPU + {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3BFD6180-7CA7-4E85-A379-225B872439A1} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} + {0B1F3656-E5B3-440C-961F-A7D004FBE9A8} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} + {48C9998C-ECE2-407F-835F-1A7255A5C99E} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E80A1224-87F5-4FEB-82AE-89006BE98B12} EndGlobalSection From 4c93ed282d1429f6f4486a5d9b30cf8e18d87671 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:14:15 +0300 Subject: [PATCH 095/126] refactor: move default configuration to sqlite --- .../{ => Sqlite}/Schedule/CampusConfiguration.cs | 6 +++--- .../{ => Sqlite}/Schedule/DisciplineConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/FacultyConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/GroupConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/LectureHallConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/LessonAssociationConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/LessonConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/ProfessorConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/SpecificWeekConfiguration.cs | 2 +- .../{ => Sqlite}/Schedule/TypeOfOccupationConfiguration.cs | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/CampusConfiguration.cs (83%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/DisciplineConfiguration.cs (96%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/FacultyConfiguration.cs (97%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/GroupConfiguration.cs (97%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/LectureHallConfiguration.cs (97%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/LessonAssociationConfiguration.cs (98%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/LessonConfiguration.cs (98%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/ProfessorConfiguration.cs (97%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/SpecificWeekConfiguration.cs (97%) rename SqlData/Persistence/EntityTypeConfigurations/{ => Sqlite}/Schedule/TypeOfOccupationConfiguration.cs (96%) diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs similarity index 83% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs index 410d6c2..99f9e83 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/CampusConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs @@ -2,13 +2,13 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; -public class CampusConfiguration : IEntityTypeConfiguration +public sealed class CampusConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Campus)); + builder.ToTable("MyCampusName"); builder.HasKey(c => c.Id); builder.HasIndex(c => c.Id).IsUnique(); builder.Property(c => c.Id).HasColumnType("INTEGER").IsRequired().ValueGeneratedOnAdd(); diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/DisciplineConfiguration.cs similarity index 96% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/DisciplineConfiguration.cs index f92ac02..94356f8 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/DisciplineConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/DisciplineConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class DisciplineConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/FacultyConfiguration.cs similarity index 97% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/FacultyConfiguration.cs index d61fce5..c7cde69 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/FacultyConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/FacultyConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class FacultyConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/GroupConfiguration.cs similarity index 97% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/GroupConfiguration.cs index 5938bb3..e4b49d0 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/GroupConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/GroupConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class GroupConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LectureHallConfiguration.cs similarity index 97% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LectureHallConfiguration.cs index 2d10805..1896d55 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LectureHallConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LectureHallConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class LectureHallConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonAssociationConfiguration.cs similarity index 98% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonAssociationConfiguration.cs index c9ecb8c..65fb513 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonAssociationConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonAssociationConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class LessonAssociationConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonConfiguration.cs similarity index 98% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonConfiguration.cs index 8d9c8cd..099c7d6 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/LessonConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/LessonConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class LessonConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/ProfessorConfiguration.cs similarity index 97% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/ProfessorConfiguration.cs index 7b95ab8..37352cc 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/ProfessorConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/ProfessorConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class ProfessorConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/SpecificWeekConfiguration.cs similarity index 97% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/SpecificWeekConfiguration.cs index f4fc8a3..cdab80d 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/SpecificWeekConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/SpecificWeekConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class SpecificWeekConfiguration : IEntityTypeConfiguration { diff --git a/SqlData/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/TypeOfOccupationConfiguration.cs similarity index 96% rename from SqlData/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs rename to SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/TypeOfOccupationConfiguration.cs index 5341a5a..6d7a2da 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Schedule/TypeOfOccupationConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/TypeOfOccupationConfiguration.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Mirea.Api.DataAccess.Domain.Schedule; -namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Schedule; +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Sqlite.Schedule; public class TypeOfOccupationConfiguration : IEntityTypeConfiguration { From 348b78b84e30070f3cfac5a7e6d3cabe01d9068d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:14:46 +0300 Subject: [PATCH 096/126] feat: add resolver for getting configuration Type --- .../Common/ConfigurationResolver.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 SqlData/Persistence/Common/ConfigurationResolver.cs diff --git a/SqlData/Persistence/Common/ConfigurationResolver.cs b/SqlData/Persistence/Common/ConfigurationResolver.cs new file mode 100644 index 0000000..907b8ca --- /dev/null +++ b/SqlData/Persistence/Common/ConfigurationResolver.cs @@ -0,0 +1,23 @@ +using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations; +using System; +using System.Linq; +using System.Reflection; + +namespace Mirea.Api.DataAccess.Persistence.Common; + +public static class ConfigurationResolver +{ + public static Type GetConfigurationType(DatabaseProvider provider) + where TEntity : class + { + var entityType = typeof(TEntity); + var providerNamespace = typeof(Mark).Namespace + "." + Enum.GetName(provider); + + var assembly = Assembly.GetExecutingAssembly(); + var configurationType = assembly.GetTypes() + .FirstOrDefault(t => t.Namespace != null && t.Namespace.StartsWith(providerNamespace) && t.Name == $"{entityType.Name}Configuration"); + + return configurationType ?? + throw new InvalidOperationException($"Configuration type not found for entity {entityType.Name} and provider {provider}."); + } +} \ No newline at end of file From 7db4dc2c869e31a4b558397c7387f6032fa59466 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:15:40 +0300 Subject: [PATCH 097/126] feat: add factory for DbContext --- .../Persistence/Common/DbContextFactory.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 SqlData/Persistence/Common/DbContextFactory.cs diff --git a/SqlData/Persistence/Common/DbContextFactory.cs b/SqlData/Persistence/Common/DbContextFactory.cs new file mode 100644 index 0000000..d8ea52d --- /dev/null +++ b/SqlData/Persistence/Common/DbContextFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using System; + +namespace Mirea.Api.DataAccess.Persistence.Common; + +public static class DbContextFactory +{ + public static DbContextOptionsBuilder CreateDbContext(this DbContextOptionsBuilder options, DatabaseProvider provider) + where TDbContext : BaseDbContext + where TEntity : class + { + var dbContext = (TDbContext)Activator.CreateInstance(typeof(TDbContext), (DbContextOptions)options.Options)!; + var configurationType = ConfigurationResolver.GetConfigurationType(provider); + var configurationInstance = (IEntityTypeConfiguration)Activator.CreateInstance(configurationType)!; + + var modelBuilder = new ModelBuilder(); + modelBuilder.ApplyConfiguration(configurationInstance); + dbContext.ApplyConfigurations(modelBuilder); + + return options; + } +} \ No newline at end of file From b8728cd4907fac550f71a2649fe5dccf40d3bdd4 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:17:36 +0300 Subject: [PATCH 098/126] feat: add factory DbContext for configuration by provider --- SqlData/Persistence/DependencyInjection.cs | 50 ++++++++++++++++------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/SqlData/Persistence/DependencyInjection.cs b/SqlData/Persistence/DependencyInjection.cs index 2ebae49..d200bfe 100644 --- a/SqlData/Persistence/DependencyInjection.cs +++ b/SqlData/Persistence/DependencyInjection.cs @@ -1,24 +1,41 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Mirea.Api.DataAccess.Application.Interfaces.DbContexts.Schedule; +using Mirea.Api.DataAccess.Domain.Schedule; +using Mirea.Api.DataAccess.Persistence.Common; using Mirea.Api.DataAccess.Persistence.Contexts.Schedule; +using Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations; +using System; +using System.Linq; +using System.Reflection; namespace Mirea.Api.DataAccess.Persistence; public static class DependencyInjection { - public static IServiceCollection AddPersistence(this IServiceCollection services, string connection) + public static IServiceCollection AddPersistence(this IServiceCollection services, DatabaseProvider dbProvider, string connection) { - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); - services.AddDbContext(DbConfig); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(DbConfig); @@ -35,6 +52,15 @@ public static class DependencyInjection return services; - void DbConfig(DbContextOptionsBuilder options) => options.UseSqlite(connection); + DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder options) + { + return dbProvider switch + { + DatabaseProvider.Sqlite => options.UseSqlite(connection), + DatabaseProvider.Mysql => options.UseMySql(connection, ServerVersion.AutoDetect(connection)), + DatabaseProvider.Postgresql => options.UseNpgsql(connection), + _ => throw new ArgumentException("Unsupported database provider", Enum.GetName(dbProvider)) + }; + } } } \ No newline at end of file From 7c79f7d8402dc82bbce99878bd8932b42f747283 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:19:58 +0300 Subject: [PATCH 099/126] feat: add wrap for generic configuration --- .../Common/ModelBuilderExtensions.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 SqlData/Persistence/Common/ModelBuilderExtensions.cs diff --git a/SqlData/Persistence/Common/ModelBuilderExtensions.cs b/SqlData/Persistence/Common/ModelBuilderExtensions.cs new file mode 100644 index 0000000..2198808 --- /dev/null +++ b/SqlData/Persistence/Common/ModelBuilderExtensions.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using System.Linq; + +namespace Mirea.Api.DataAccess.Persistence.Common; + +public static class ModelBuilderExtensions +{ + public static void ApplyConfiguration(this ModelBuilder modelBuilder, object configuration) + { + var applyGenericMethod = typeof(ModelBuilder) + .GetMethods() + .First(m => m.Name == "ApplyConfiguration" && + m.GetParameters().Any(p => p.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))); + + var entityType = configuration.GetType().GetInterfaces() + .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) + .GetGenericArguments()[0]; + + var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(entityType); + applyConcreteMethod.Invoke(modelBuilder, [configuration]); + } +} From 271df127a6eb80923e49b53d5dd24d04bc1bf109 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:20:20 +0300 Subject: [PATCH 100/126] feat: add DbContext for UberDbContext --- SqlData/Persistence/DependencyInjection.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/SqlData/Persistence/DependencyInjection.cs b/SqlData/Persistence/DependencyInjection.cs index d200bfe..ad7c511 100644 --- a/SqlData/Persistence/DependencyInjection.cs +++ b/SqlData/Persistence/DependencyInjection.cs @@ -36,8 +36,28 @@ public static class DependencyInjection services.AddDbContext(options => UseDatabase(options).CreateDbContext(dbProvider)); + services.AddDbContext(options => + { + var providerNamespace = typeof(Mark).Namespace + "." + Enum.GetName(dbProvider); - services.AddDbContext(DbConfig); + var assembly = Assembly.GetExecutingAssembly(); + var configurationTypes = assembly.GetTypes() + .Where(t => + t is { IsNested: false, IsAbstract: false, Namespace: not null } && + t.Namespace.StartsWith(providerNamespace) && + t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))); + + var modelBuilder = new ModelBuilder(); + + foreach (var configurationType in configurationTypes) + { + var configurationInstance = Activator.CreateInstance(configurationType)!; + modelBuilder.ApplyConfiguration(configurationInstance); + } + + var dbContext = (UberDbContext)Activator.CreateInstance(typeof(UberDbContext), (DbContextOptions)UseDatabase(options).Options)!; + dbContext.ApplyConfigurations(modelBuilder); + }); services.AddScoped(provider => provider.GetRequiredService()); services.AddScoped(provider => provider.GetRequiredService()); From 62ccf942229f767cc951bfe0320ba4a98cc1fb53 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:25:21 +0300 Subject: [PATCH 101/126] feat: add converter DatabaseEnum to DatabaseProvider --- .../General/Settings/DbSettings.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Endpoint/Configuration/General/Settings/DbSettings.cs b/Endpoint/Configuration/General/Settings/DbSettings.cs index f8ae35e..2fa5ff9 100644 --- a/Endpoint/Configuration/General/Settings/DbSettings.cs +++ b/Endpoint/Configuration/General/Settings/DbSettings.cs @@ -1,4 +1,6 @@ -using Mirea.Api.Endpoint.Configuration.General.Attributes; +using System; +using Mirea.Api.DataAccess.Persistence.Common; +using Mirea.Api.Endpoint.Configuration.General.Attributes; using Mirea.Api.Endpoint.Configuration.General.Interfaces; namespace Mirea.Api.Endpoint.Configuration.General.Settings; @@ -15,8 +17,15 @@ public class DbSettings : IIsConfigured public DatabaseEnum TypeDatabase { get; set; } public required string ConnectionStringSql { get; set; } - public bool IsConfigured() - { - return !string.IsNullOrEmpty(ConnectionStringSql); - } + public DatabaseProvider DatabaseProvider => + TypeDatabase switch + { + DatabaseEnum.PostgresSql => DatabaseProvider.Postgresql, + DatabaseEnum.Mysql => DatabaseProvider.Mysql, + DatabaseEnum.Sqlite => DatabaseProvider.Sqlite, + _ => throw new ArgumentOutOfRangeException() + }; + + public bool IsConfigured() => + !string.IsNullOrEmpty(ConnectionStringSql); } \ No newline at end of file From 8a103831ebfe77b27aa525f2c709305950466464 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:26:42 +0300 Subject: [PATCH 102/126] refactor: send provider --- Endpoint/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 840504f..3c39eed 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -115,7 +115,7 @@ public class Program builder.Services.Configure(builder.Configuration); builder.Services.AddApplication(); - builder.Services.AddPersistence(builder.Configuration.Get()?.DbSettings?.ConnectionStringSql ?? string.Empty); + builder.Services.AddPersistence(generalConfig?.DbSettings?.DatabaseProvider ?? DatabaseProvider.Sqlite, generalConfig?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); builder.Services.AddSingleton(); From b81fe6d8c17b212b14a8094213fcbc163c9b3ba0 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 20:29:11 +0300 Subject: [PATCH 103/126] fix: rename table --- .../Sqlite/Schedule/CampusConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs index 99f9e83..3a85a0a 100644 --- a/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs +++ b/SqlData/Persistence/EntityTypeConfigurations/Sqlite/Schedule/CampusConfiguration.cs @@ -8,7 +8,7 @@ public sealed class CampusConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable("MyCampusName"); + builder.ToTable(nameof(Campus)); builder.HasKey(c => c.Id); builder.HasIndex(c => c.Id).IsUnique(); builder.Property(c => c.Id).HasColumnType("INTEGER").IsRequired().ValueGeneratedOnAdd(); From a353b4c3f8cd711513234ed139a8af01bed0c8e9 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 21:19:54 +0300 Subject: [PATCH 104/126] test: upgrade test --- .gitea/workflows/test.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index 49484d4..9d784b5 100644 --- a/.gitea/workflows/test.yaml +++ b/.gitea/workflows/test.yaml @@ -2,17 +2,19 @@ name: .NET Test Pipeline on: pull_request: - branches: [master, 'release/*'] + push: + branches: + [master, 'release/*'] jobs: build-and-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x From aa1e1000fac528b0dd92f1f6dca6aab5fbde0981 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 21:45:27 +0300 Subject: [PATCH 105/126] fix: set new path to projects --- Endpoint/Endpoint.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Endpoint/Endpoint.csproj b/Endpoint/Endpoint.csproj index 985a730..2aad3c8 100644 --- a/Endpoint/Endpoint.csproj +++ b/Endpoint/Endpoint.csproj @@ -31,8 +31,8 @@ - - + + From c9c6a99fe98b16a8f8f210504561873950a5f32d Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 21:45:45 +0300 Subject: [PATCH 106/126] test: trying to set up tests to test application in different databases --- .gitea/workflows/Settings.json | 51 +++++++++++++++++++ .gitea/workflows/database_test.yaml | 77 +++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 .gitea/workflows/Settings.json create mode 100644 .gitea/workflows/database_test.yaml diff --git a/.gitea/workflows/Settings.json b/.gitea/workflows/Settings.json new file mode 100644 index 0000000..c1b7848 --- /dev/null +++ b/.gitea/workflows/Settings.json @@ -0,0 +1,51 @@ +{ + "DbSettings": { + "TypeDatabase": 1, + "ConnectionStringSql": "Data Source=database.db3" + }, + "CacheSettings": { + "TypeDatabase": 0, + "ConnectionString": null + }, + "ScheduleSettings": { + "CronUpdateSchedule": "0 */6 * * *", + "StartTerm": "2024-02-05", + "PairPeriod": { + "1": { + "Start": "09:00:00", + "End": "10:30:00" + }, + "2": { + "Start": "10:40:00", + "End": "12:10:00" + }, + "3": { + "Start": "12:40:00", + "End": "14:10:00" + }, + "4": { + "Start": "14:20:00", + "End": "15:50:00" + }, + "5": { + "Start": "16:20:00", + "End": "17:50:00" + }, + "6": { + "Start": "18:00:00", + "End": "19:30:00" + }, + "7": { + "Start": "19:40:00", + "End": "21:10:00" + } + } + }, + "EmailSettings": { + }, + "LogSettings": { + "EnableLogToFile": false, + "LogFilePath": null, + "LogFileName": null + } +} \ No newline at end of file diff --git a/.gitea/workflows/database_test.yaml b/.gitea/workflows/database_test.yaml new file mode 100644 index 0000000..1a1c5d0 --- /dev/null +++ b/.gitea/workflows/database_test.yaml @@ -0,0 +1,77 @@ +name: Test with Different Databases + +on: + pull_request: + push: + branches: + [master, 'release/*'] + +jobs: + test: + strategy: + matrix: + db-provider: [sqlite, mysql, postgresql] + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:latest + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: testdb + options: >- + --health-cmd "mysqladmin ping --silent" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v4 + + - name: Set up .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Restore dependencies + run: dotnet restore + + - name: Build the solution + run: dotnet build --configuration Release + + - name: Install jq + run: sudo apt-get install -y jq + + - name: Modify configuration for SQLite + if: matrix.db-provider == 'sqlite' + run: | + jq '.DbSettings.TypeDatabase = 1 | .DbSettings.ConnectionStringSql = "Data Source=test.db3"' Settings.json > temp.json && mv temp.json Settings.json + + - name: Modify configuration for MySQL + if: matrix.db-provider == 'mysql' + run: | + jq '.DbSettings.TypeDatabase = 0 | .DbSettings.ConnectionStringSql = "Server=127.0.0.1;Port=3306;Database=testdb;Uid=root;Pwd=root;"' Settings.json > temp.json && mv temp.json Settings.json + + - name: Modify configuration for PostgreSQL + if: matrix.db-provider == 'postgresql' + run: | + jq '.DbSettings.TypeDatabase = 2 | .DbSettings.ConnectionStringSql = "Host=127.0.0.1;Port=5432;Database=testdb;Username=postgres;Password=postgres"' Settings.json > temp.json && mv temp.json Settings.json + + - name: Run tests + run: dotnet test --configuration Release --no-build --no-restore --verbosity normal \ No newline at end of file From f0544ff42e2381283cdc93b615b78993d5252776 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 23:30:11 +0300 Subject: [PATCH 107/126] fix: error CS0103 --- Endpoint/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index 3c39eed..adef11a 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -15,6 +15,7 @@ using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Common.Services.Security; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; +using Mirea.Api.Endpoint.Configuration.General.Settings; using Mirea.Api.Endpoint.Configuration.General.Validators; using Mirea.Api.Endpoint.Configuration.Swagger; using Mirea.Api.Endpoint.Middleware; @@ -114,6 +115,7 @@ public class Program builder.Configuration.AddJsonFile(PathBuilder.Combine(GeneralConfig.FilePath), optional: true, reloadOnChange: true); builder.Services.Configure(builder.Configuration); + var generalConfig = builder.Configuration.Get(); builder.Services.AddApplication(); builder.Services.AddPersistence(generalConfig?.DbSettings?.DatabaseProvider ?? DatabaseProvider.Sqlite, generalConfig?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); From 815c860dc08b925e132436415a3e656fc923ffc2 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Thu, 30 May 2024 23:58:24 +0300 Subject: [PATCH 108/126] fix: error CS0103 --- Endpoint/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs index adef11a..87756d9 100644 --- a/Endpoint/Program.cs +++ b/Endpoint/Program.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; +using Mirea.Api.DataAccess.Persistence.Common; using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Common.Services.Security; From b67f0a82ede82dfc22a41977a177bef0ae02d31b Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Fri, 31 May 2024 01:16:13 +0300 Subject: [PATCH 109/126] test: fix port --- .gitea/workflows/database_test.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/database_test.yaml b/.gitea/workflows/database_test.yaml index 1a1c5d0..35c03db 100644 --- a/.gitea/workflows/database_test.yaml +++ b/.gitea/workflows/database_test.yaml @@ -25,7 +25,7 @@ jobs: --health-timeout 5s --health-retries 5 ports: - - 3306:3306 + - 5555:3306 postgres: image: postgres:latest @@ -39,7 +39,7 @@ jobs: --health-timeout 5s --health-retries 5 ports: - - 5432:5432 + - 6666:5432 steps: - uses: actions/checkout@v4 @@ -66,12 +66,12 @@ jobs: - name: Modify configuration for MySQL if: matrix.db-provider == 'mysql' run: | - jq '.DbSettings.TypeDatabase = 0 | .DbSettings.ConnectionStringSql = "Server=127.0.0.1;Port=3306;Database=testdb;Uid=root;Pwd=root;"' Settings.json > temp.json && mv temp.json Settings.json + jq '.DbSettings.TypeDatabase = 0 | .DbSettings.ConnectionStringSql = "Server=127.0.0.1;Port=5555;Database=testdb;Uid=root;Pwd=root;"' Settings.json > temp.json && mv temp.json Settings.json - name: Modify configuration for PostgreSQL if: matrix.db-provider == 'postgresql' run: | - jq '.DbSettings.TypeDatabase = 2 | .DbSettings.ConnectionStringSql = "Host=127.0.0.1;Port=5432;Database=testdb;Username=postgres;Password=postgres"' Settings.json > temp.json && mv temp.json Settings.json + jq '.DbSettings.TypeDatabase = 2 | .DbSettings.ConnectionStringSql = "Host=127.0.0.1;Port=6666;Database=testdb;Username=postgres;Password=postgres"' Settings.json > temp.json && mv temp.json Settings.json - name: Run tests run: dotnet test --configuration Release --no-build --no-restore --verbosity normal \ No newline at end of file From f17ee43805ab7855809b379e9f70f284d004f97a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Fri, 31 May 2024 01:25:22 +0300 Subject: [PATCH 110/126] test: change docker to apt --- .gitea/workflows/database_test.yaml | 48 ++++++++++++----------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/.gitea/workflows/database_test.yaml b/.gitea/workflows/database_test.yaml index 35c03db..adf2637 100644 --- a/.gitea/workflows/database_test.yaml +++ b/.gitea/workflows/database_test.yaml @@ -12,35 +12,7 @@ jobs: matrix: db-provider: [sqlite, mysql, postgresql] runs-on: ubuntu-latest - - services: - mysql: - image: mysql:latest - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: testdb - options: >- - --health-cmd "mysqladmin ping --silent" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5555:3306 - - postgres: - image: postgres:latest - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: testdb - options: >- - --health-cmd "pg_isready -U postgres" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 6666:5432 - + steps: - uses: actions/checkout@v4 @@ -68,10 +40,28 @@ jobs: run: | jq '.DbSettings.TypeDatabase = 0 | .DbSettings.ConnectionStringSql = "Server=127.0.0.1;Port=5555;Database=testdb;Uid=root;Pwd=root;"' Settings.json > temp.json && mv temp.json Settings.json + - name: Install MySQL + if: matrix.db-provider == 'mysql' + run: | + sudo apt-get update + sudo apt-get install -y mysql-server + sudo service mysql start + sudo mysql -e "CREATE DATABASE testdb; CREATE USER 'root'@'localhost' IDENTIFIED BY 'root'; GRANT ALL PRIVILEGES ON testdb.* TO 'root'@'localhost'; FLUSH PRIVILEGES;" + - name: Modify configuration for PostgreSQL if: matrix.db-provider == 'postgresql' run: | jq '.DbSettings.TypeDatabase = 2 | .DbSettings.ConnectionStringSql = "Host=127.0.0.1;Port=6666;Database=testdb;Username=postgres;Password=postgres"' Settings.json > temp.json && mv temp.json Settings.json + - name: Install PostgreSQL + if: matrix.db-provider == 'postgresql' + run: | + sudo apt-get update + sudo apt-get install -y postgresql postgresql-contrib + sudo service postgresql start + sudo -u postgres psql -c "CREATE DATABASE testdb;" + sudo -u postgres psql -c "CREATE USER postgres WITH PASSWORD 'postgres';" + sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE testdb TO postgres;" + - name: Run tests run: dotnet test --configuration Release --no-build --no-restore --verbosity normal \ No newline at end of file From 7a741d7783462b3dedeff3d263485aecde144e67 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Fri, 31 May 2024 01:37:03 +0300 Subject: [PATCH 111/126] test: remove testing --- .gitea/workflows/Settings.json | 51 ---------------------- .gitea/workflows/database_test.yaml | 67 ----------------------------- 2 files changed, 118 deletions(-) delete mode 100644 .gitea/workflows/Settings.json delete mode 100644 .gitea/workflows/database_test.yaml diff --git a/.gitea/workflows/Settings.json b/.gitea/workflows/Settings.json deleted file mode 100644 index c1b7848..0000000 --- a/.gitea/workflows/Settings.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "DbSettings": { - "TypeDatabase": 1, - "ConnectionStringSql": "Data Source=database.db3" - }, - "CacheSettings": { - "TypeDatabase": 0, - "ConnectionString": null - }, - "ScheduleSettings": { - "CronUpdateSchedule": "0 */6 * * *", - "StartTerm": "2024-02-05", - "PairPeriod": { - "1": { - "Start": "09:00:00", - "End": "10:30:00" - }, - "2": { - "Start": "10:40:00", - "End": "12:10:00" - }, - "3": { - "Start": "12:40:00", - "End": "14:10:00" - }, - "4": { - "Start": "14:20:00", - "End": "15:50:00" - }, - "5": { - "Start": "16:20:00", - "End": "17:50:00" - }, - "6": { - "Start": "18:00:00", - "End": "19:30:00" - }, - "7": { - "Start": "19:40:00", - "End": "21:10:00" - } - } - }, - "EmailSettings": { - }, - "LogSettings": { - "EnableLogToFile": false, - "LogFilePath": null, - "LogFileName": null - } -} \ No newline at end of file diff --git a/.gitea/workflows/database_test.yaml b/.gitea/workflows/database_test.yaml deleted file mode 100644 index adf2637..0000000 --- a/.gitea/workflows/database_test.yaml +++ /dev/null @@ -1,67 +0,0 @@ -name: Test with Different Databases - -on: - pull_request: - push: - branches: - [master, 'release/*'] - -jobs: - test: - strategy: - matrix: - db-provider: [sqlite, mysql, postgresql] - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Restore dependencies - run: dotnet restore - - - name: Build the solution - run: dotnet build --configuration Release - - - name: Install jq - run: sudo apt-get install -y jq - - - name: Modify configuration for SQLite - if: matrix.db-provider == 'sqlite' - run: | - jq '.DbSettings.TypeDatabase = 1 | .DbSettings.ConnectionStringSql = "Data Source=test.db3"' Settings.json > temp.json && mv temp.json Settings.json - - - name: Modify configuration for MySQL - if: matrix.db-provider == 'mysql' - run: | - jq '.DbSettings.TypeDatabase = 0 | .DbSettings.ConnectionStringSql = "Server=127.0.0.1;Port=5555;Database=testdb;Uid=root;Pwd=root;"' Settings.json > temp.json && mv temp.json Settings.json - - - name: Install MySQL - if: matrix.db-provider == 'mysql' - run: | - sudo apt-get update - sudo apt-get install -y mysql-server - sudo service mysql start - sudo mysql -e "CREATE DATABASE testdb; CREATE USER 'root'@'localhost' IDENTIFIED BY 'root'; GRANT ALL PRIVILEGES ON testdb.* TO 'root'@'localhost'; FLUSH PRIVILEGES;" - - - name: Modify configuration for PostgreSQL - if: matrix.db-provider == 'postgresql' - run: | - jq '.DbSettings.TypeDatabase = 2 | .DbSettings.ConnectionStringSql = "Host=127.0.0.1;Port=6666;Database=testdb;Username=postgres;Password=postgres"' Settings.json > temp.json && mv temp.json Settings.json - - - name: Install PostgreSQL - if: matrix.db-provider == 'postgresql' - run: | - sudo apt-get update - sudo apt-get install -y postgresql postgresql-contrib - sudo service postgresql start - sudo -u postgres psql -c "CREATE DATABASE testdb;" - sudo -u postgres psql -c "CREATE USER postgres WITH PASSWORD 'postgres';" - sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE testdb TO postgres;" - - - name: Run tests - run: dotnet test --configuration Release --no-build --no-restore --verbosity normal \ No newline at end of file From 04b66871815f75fd1e342f3bee05dbfe427b7a9f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:39:02 +0300 Subject: [PATCH 112/126] feat: add mysql configurations --- .../Mysql/Schedule/CampusConfiguration.cs | 20 ++++++++ .../Mysql/Schedule/DisciplineConfiguration.cs | 18 +++++++ .../Mysql/Schedule/FacultyConfiguration.cs | 26 ++++++++++ .../Mysql/Schedule/GroupConfiguration.cs | 25 ++++++++++ .../Schedule/LectureHallConfiguration.cs | 25 ++++++++++ .../LessonAssociationConfiguration.cs | 48 +++++++++++++++++++ .../Mysql/Schedule/LessonConfiguration.cs | 36 ++++++++++++++ .../Mysql/Schedule/ProfessorConfiguration.cs | 19 ++++++++ .../Schedule/SpecificWeekConfiguration.cs | 26 ++++++++++ .../Schedule/TypeOfOccupationConfiguration.cs | 18 +++++++ 10 files changed, 261 insertions(+) create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/CampusConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/DisciplineConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/FacultyConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/GroupConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LectureHallConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonAssociationConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/ProfessorConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/SpecificWeekConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/TypeOfOccupationConfiguration.cs diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/CampusConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/CampusConfiguration.cs new file mode 100644 index 0000000..a6a961d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/CampusConfiguration.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public sealed class CampusConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Campus)); + builder.HasKey(c => c.Id); + builder.HasIndex(c => c.Id).IsUnique(); + builder.Property(c => c.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(c => c.Address).HasColumnType("VARCHAR(512)").HasMaxLength(512); + builder.Property(c => c.CodeName).HasColumnType("VARCHAR(16)").IsRequired().HasMaxLength(16); + builder.Property(c => c.FullName).HasColumnType("VARCHAR(256)").HasMaxLength(256); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/DisciplineConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/DisciplineConfiguration.cs new file mode 100644 index 0000000..ababf1f --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/DisciplineConfiguration.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class DisciplineConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Discipline)); + builder.HasKey(d => d.Id); + builder.HasIndex(d => d.Id).IsUnique(); + builder.Property(d => d.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(d => d.Name).HasColumnType("VARCHAR(256)").HasMaxLength(256).IsRequired(); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/FacultyConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/FacultyConfiguration.cs new file mode 100644 index 0000000..3e2e24d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/FacultyConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class FacultyConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Faculty)); + builder.HasKey(f => f.Id); + builder.HasIndex(f => f.Id).IsUnique(); + builder.Property(f => f.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(f => f.Name).HasColumnType("VARCHAR(256)").IsRequired().HasMaxLength(256); + + builder.Property(f => f.CampusId).HasColumnType("INT"); + + builder + .HasOne(f => f.Campus) + .WithMany(c => c.Faculties) + .HasForeignKey(c => c.CampusId) + .OnDelete(DeleteBehavior.SetNull); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/GroupConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/GroupConfiguration.cs new file mode 100644 index 0000000..a0e1645 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/GroupConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class GroupConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Group)); + builder.HasKey(g => g.Id); + builder.HasIndex(g => g.Id).IsUnique(); + builder.Property(g => g.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(g => g.FacultyId).HasColumnType("INT"); + builder.Property(g => g.Name).HasColumnType("VARCHAR(64)").IsRequired().HasMaxLength(64); + + builder + .HasOne(g => g.Faculty) + .WithMany(u => u.Groups) + .HasForeignKey(d => d.FacultyId) + .OnDelete(DeleteBehavior.SetNull); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LectureHallConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LectureHallConfiguration.cs new file mode 100644 index 0000000..ec9ab8c --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LectureHallConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class LectureHallConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(LectureHall)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.CampusId).HasColumnType("INT").IsRequired(); + builder.Property(l => l.Name).HasColumnType("VARCHAR(64)").IsRequired().HasMaxLength(64); + + builder + .HasOne(l => l.Campus) + .WithMany(c => c.LectureHalls) + .HasForeignKey(d => d.CampusId) + .OnDelete(DeleteBehavior.Restrict); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonAssociationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonAssociationConfiguration.cs new file mode 100644 index 0000000..f35317d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonAssociationConfiguration.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class LessonAssociationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(LessonAssociation)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.LinkToMeet).HasColumnType("VARCHAR(512)").HasMaxLength(512); + + builder.Property(l => l.LessonId).HasColumnType("INT").IsRequired(); + builder.Property(l => l.ProfessorId).HasColumnType("INT"); + builder.Property(l => l.LectureHallId).HasColumnType("INT"); + builder.Property(l => l.TypeOfOccupationId).HasColumnType("INT").IsRequired(); + + + builder + .HasOne(l => l.Lesson) + .WithMany(d => d.LessonAssociations) + .HasForeignKey(l => l.LessonId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasOne(l => l.Professor) + .WithMany(p => p.LessonAssociations) + .HasForeignKey(l => l.ProfessorId) + .OnDelete(DeleteBehavior.SetNull); + + builder + .HasOne(l => l.LectureHall) + .WithMany(l => l.LessonAssociations) + .HasForeignKey(l => l.LectureHallId) + .OnDelete(DeleteBehavior.SetNull); + + builder + .HasOne(l => l.TypeOfOccupation) + .WithMany(t => t.Lessons) + .HasForeignKey(d => d.TypeOfOccupationId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonConfiguration.cs new file mode 100644 index 0000000..66e76d8 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/LessonConfiguration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class LessonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Lesson)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.IsEven).HasColumnType("BIT(1)").IsRequired(); + builder.Property(l => l.DayOfWeek).HasColumnType("INT").IsRequired(); + builder.Property(l => l.PairNumber).HasColumnType("INT").IsRequired(); + builder.Property(l => l.IsExcludedWeeks).HasColumnType("BIT(1)"); + + builder.Property(l => l.GroupId).HasColumnType("INT").IsRequired(); + builder.Property(l => l.DisciplineId).HasColumnType("INT").IsRequired(); + + builder + .HasOne(l => l.Group) + .WithMany(g => g.Lessons) + .HasForeignKey(d => d.GroupId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasOne(l => l.Discipline) + .WithMany(d => d.Lessons) + .HasForeignKey(l => l.DisciplineId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/ProfessorConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/ProfessorConfiguration.cs new file mode 100644 index 0000000..a7e8f02 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/ProfessorConfiguration.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class ProfessorConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Professor)); + builder.HasKey(p => p.Id); + builder.HasIndex(p => p.Id).IsUnique(); + builder.Property(p => p.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(p => p.Name).HasColumnType("VARCHAR").IsRequired(); + builder.Property(p => p.AltName).HasColumnType("VARCHAR"); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/SpecificWeekConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/SpecificWeekConfiguration.cs new file mode 100644 index 0000000..e7b0529 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/SpecificWeekConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class SpecificWeekConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(SpecificWeek)); + builder.HasKey(s => s.Id); + builder.HasIndex(s => s.Id).IsUnique(); + builder.Property(s => s.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(s => s.WeekNumber).HasColumnType("INT").IsRequired(); + + builder.Property(s => s.LessonId).HasColumnType("INT").IsRequired(); + + builder + .HasOne(s => s.Lesson) + .WithMany(l => l.SpecificWeeks) + .HasForeignKey(s => s.LessonId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/TypeOfOccupationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/TypeOfOccupationConfiguration.cs new file mode 100644 index 0000000..90e92e5 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Mysql/Schedule/TypeOfOccupationConfiguration.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Mysql.Schedule; + +public class TypeOfOccupationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(TypeOfOccupation)); + builder.HasKey(t => t.Id); + builder.HasIndex(t => t.Id).IsUnique(); + builder.Property(t => t.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(t => t.ShortName).HasColumnType("VARCHAR").IsRequired().HasMaxLength(16); + } +} \ No newline at end of file From 7aa37618e0c36814cb8f7c3de29b2c902005ec6f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:39:25 +0300 Subject: [PATCH 113/126] feat: add postgresql configurations --- .../Schedule/CampusConfiguration.cs | 20 ++++++++ .../Schedule/DisciplineConfiguration.cs | 18 +++++++ .../Schedule/FacultyConfiguration.cs | 26 ++++++++++ .../Postgresql/Schedule/GroupConfiguration.cs | 25 ++++++++++ .../Schedule/LectureHallConfiguration.cs | 25 ++++++++++ .../LessonAssociationConfiguration.cs | 48 +++++++++++++++++++ .../Schedule/LessonConfiguration.cs | 36 ++++++++++++++ .../Schedule/ProfessorConfiguration.cs | 19 ++++++++ .../Schedule/SpecificWeekConfiguration.cs | 26 ++++++++++ .../Schedule/TypeOfOccupationConfiguration.cs | 18 +++++++ 10 files changed, 261 insertions(+) create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/CampusConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/DisciplineConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/FacultyConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/GroupConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LectureHallConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonAssociationConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/ProfessorConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/SpecificWeekConfiguration.cs create mode 100644 SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/TypeOfOccupationConfiguration.cs diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/CampusConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/CampusConfiguration.cs new file mode 100644 index 0000000..055be55 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/CampusConfiguration.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public sealed class CampusConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Campus)); + builder.HasKey(c => c.Id); + builder.HasIndex(c => c.Id).IsUnique(); + builder.Property(c => c.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(c => c.Address).HasColumnType("text").HasMaxLength(512); + builder.Property(c => c.CodeName).HasColumnType("text").IsRequired().HasMaxLength(16); + builder.Property(c => c.FullName).HasColumnType("text").HasMaxLength(256); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/DisciplineConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/DisciplineConfiguration.cs new file mode 100644 index 0000000..01ff876 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/DisciplineConfiguration.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class DisciplineConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Discipline)); + builder.HasKey(d => d.Id); + builder.HasIndex(d => d.Id).IsUnique(); + builder.Property(d => d.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(d => d.Name).HasColumnType("text").HasMaxLength(256).IsRequired(); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/FacultyConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/FacultyConfiguration.cs new file mode 100644 index 0000000..05f622f --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/FacultyConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class FacultyConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Faculty)); + builder.HasKey(f => f.Id); + builder.HasIndex(f => f.Id).IsUnique(); + builder.Property(f => f.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(f => f.Name).HasColumnType("text").IsRequired().HasMaxLength(256); + + builder.Property(f => f.CampusId).HasColumnType("serial"); + + builder + .HasOne(f => f.Campus) + .WithMany(c => c.Faculties) + .HasForeignKey(c => c.CampusId) + .OnDelete(DeleteBehavior.SetNull); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/GroupConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/GroupConfiguration.cs new file mode 100644 index 0000000..1ee501d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/GroupConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class GroupConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Group)); + builder.HasKey(g => g.Id); + builder.HasIndex(g => g.Id).IsUnique(); + builder.Property(g => g.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(g => g.FacultyId).HasColumnType("serial"); + builder.Property(g => g.Name).HasColumnType("text").IsRequired().HasMaxLength(64); + + builder + .HasOne(g => g.Faculty) + .WithMany(u => u.Groups) + .HasForeignKey(d => d.FacultyId) + .OnDelete(DeleteBehavior.SetNull); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LectureHallConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LectureHallConfiguration.cs new file mode 100644 index 0000000..039df3d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LectureHallConfiguration.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class LectureHallConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(LectureHall)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("INT").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.CampusId).HasColumnType("INT").IsRequired(); + builder.Property(l => l.Name).HasColumnType("VARCHAR(64)").IsRequired().HasMaxLength(64); + + builder + .HasOne(l => l.Campus) + .WithMany(c => c.LectureHalls) + .HasForeignKey(d => d.CampusId) + .OnDelete(DeleteBehavior.Restrict); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonAssociationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonAssociationConfiguration.cs new file mode 100644 index 0000000..aef538a --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonAssociationConfiguration.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class LessonAssociationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(LessonAssociation)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.LinkToMeet).HasColumnType("text").HasMaxLength(512); + + builder.Property(l => l.LessonId).HasColumnType("serial").IsRequired(); + builder.Property(l => l.ProfessorId).HasColumnType("serial"); + builder.Property(l => l.LectureHallId).HasColumnType("serial"); + builder.Property(l => l.TypeOfOccupationId).HasColumnType("serial").IsRequired(); + + + builder + .HasOne(l => l.Lesson) + .WithMany(d => d.LessonAssociations) + .HasForeignKey(l => l.LessonId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasOne(l => l.Professor) + .WithMany(p => p.LessonAssociations) + .HasForeignKey(l => l.ProfessorId) + .OnDelete(DeleteBehavior.SetNull); + + builder + .HasOne(l => l.LectureHall) + .WithMany(l => l.LessonAssociations) + .HasForeignKey(l => l.LectureHallId) + .OnDelete(DeleteBehavior.SetNull); + + builder + .HasOne(l => l.TypeOfOccupation) + .WithMany(t => t.Lessons) + .HasForeignKey(d => d.TypeOfOccupationId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonConfiguration.cs new file mode 100644 index 0000000..a47076d --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/LessonConfiguration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class LessonConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Lesson)); + builder.HasKey(l => l.Id); + builder.HasIndex(l => l.Id).IsUnique(); + builder.Property(l => l.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(l => l.IsEven).HasColumnType("boolean").IsRequired(); + builder.Property(l => l.DayOfWeek).HasColumnType("serial").IsRequired(); + builder.Property(l => l.PairNumber).HasColumnType("serial").IsRequired(); + builder.Property(l => l.IsExcludedWeeks).HasColumnType("boolean"); + + builder.Property(l => l.GroupId).HasColumnType("serial").IsRequired(); + builder.Property(l => l.DisciplineId).HasColumnType("serial").IsRequired(); + + builder + .HasOne(l => l.Group) + .WithMany(g => g.Lessons) + .HasForeignKey(d => d.GroupId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasOne(l => l.Discipline) + .WithMany(d => d.Lessons) + .HasForeignKey(l => l.DisciplineId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/ProfessorConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/ProfessorConfiguration.cs new file mode 100644 index 0000000..9779cde --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/ProfessorConfiguration.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class ProfessorConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Professor)); + builder.HasKey(p => p.Id); + builder.HasIndex(p => p.Id).IsUnique(); + builder.Property(p => p.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(p => p.Name).HasColumnType("text").IsRequired(); + builder.Property(p => p.AltName).HasColumnType("text"); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/SpecificWeekConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/SpecificWeekConfiguration.cs new file mode 100644 index 0000000..6415c7b --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/SpecificWeekConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class SpecificWeekConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(SpecificWeek)); + builder.HasKey(s => s.Id); + builder.HasIndex(s => s.Id).IsUnique(); + builder.Property(s => s.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(s => s.WeekNumber).HasColumnType("serial").IsRequired(); + + builder.Property(s => s.LessonId).HasColumnType("serial").IsRequired(); + + builder + .HasOne(s => s.Lesson) + .WithMany(l => l.SpecificWeeks) + .HasForeignKey(s => s.LessonId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/TypeOfOccupationConfiguration.cs b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/TypeOfOccupationConfiguration.cs new file mode 100644 index 0000000..af53715 --- /dev/null +++ b/SqlData/Persistence/EntityTypeConfigurations/Postgresql/Schedule/TypeOfOccupationConfiguration.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mirea.Api.DataAccess.Domain.Schedule; + +namespace Mirea.Api.DataAccess.Persistence.EntityTypeConfigurations.Postgresql.Schedule; + +public class TypeOfOccupationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(TypeOfOccupation)); + builder.HasKey(t => t.Id); + builder.HasIndex(t => t.Id).IsUnique(); + builder.Property(t => t.Id).HasColumnType("serial").IsRequired().ValueGeneratedOnAdd(); + + builder.Property(t => t.ShortName).HasColumnType("text").IsRequired().HasMaxLength(16); + } +} \ No newline at end of file From ae4682368546a737d8981ad71438cfec5ae3e238 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:40:09 +0300 Subject: [PATCH 114/126] build: upgrade dependencies --- SqlData/Persistence/Persistence.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SqlData/Persistence/Persistence.csproj b/SqlData/Persistence/Persistence.csproj index d191edd..07ca710 100644 --- a/SqlData/Persistence/Persistence.csproj +++ b/SqlData/Persistence/Persistence.csproj @@ -13,8 +13,8 @@ - - + + From 8cd8277c22defc9edf93113fbbb52893bc5eddc7 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:40:48 +0300 Subject: [PATCH 115/126] feat: add assembly for migration --- .../MysqlMigrations/MysqlMigrations.csproj | 12 ++++++++++++ .../Migrations/PsqlMigrations/PsqlMigrations.csproj | 12 ++++++++++++ .../SqliteMigrations/SqliteMigrations.csproj | 12 ++++++++++++ SqlData/Persistence/DependencyInjection.cs | 9 ++++++--- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 SqlData/Migrations/MysqlMigrations/MysqlMigrations.csproj create mode 100644 SqlData/Migrations/PsqlMigrations/PsqlMigrations.csproj create mode 100644 SqlData/Migrations/SqliteMigrations/SqliteMigrations.csproj diff --git a/SqlData/Migrations/MysqlMigrations/MysqlMigrations.csproj b/SqlData/Migrations/MysqlMigrations/MysqlMigrations.csproj new file mode 100644 index 0000000..d9d83f2 --- /dev/null +++ b/SqlData/Migrations/MysqlMigrations/MysqlMigrations.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + ..\..\Persistence\bin\ + + + + + + + diff --git a/SqlData/Migrations/PsqlMigrations/PsqlMigrations.csproj b/SqlData/Migrations/PsqlMigrations/PsqlMigrations.csproj new file mode 100644 index 0000000..d9d83f2 --- /dev/null +++ b/SqlData/Migrations/PsqlMigrations/PsqlMigrations.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + ..\..\Persistence\bin\ + + + + + + + diff --git a/SqlData/Migrations/SqliteMigrations/SqliteMigrations.csproj b/SqlData/Migrations/SqliteMigrations/SqliteMigrations.csproj new file mode 100644 index 0000000..d9d83f2 --- /dev/null +++ b/SqlData/Migrations/SqliteMigrations/SqliteMigrations.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + ..\..\Persistence\bin\ + + + + + + + diff --git a/SqlData/Persistence/DependencyInjection.cs b/SqlData/Persistence/DependencyInjection.cs index ad7c511..f86cd87 100644 --- a/SqlData/Persistence/DependencyInjection.cs +++ b/SqlData/Persistence/DependencyInjection.cs @@ -76,9 +76,12 @@ public static class DependencyInjection { return dbProvider switch { - DatabaseProvider.Sqlite => options.UseSqlite(connection), - DatabaseProvider.Mysql => options.UseMySql(connection, ServerVersion.AutoDetect(connection)), - DatabaseProvider.Postgresql => options.UseNpgsql(connection), + DatabaseProvider.Mysql => options.UseMySql(connection, ServerVersion.AutoDetect(connection), + x => x.MigrationsAssembly("MysqlMigrations")), + DatabaseProvider.Sqlite => options.UseSqlite(connection, + x => x.MigrationsAssembly("SqliteMigrations")), + DatabaseProvider.Postgresql => options.UseNpgsql(connection, + x => x.MigrationsAssembly("PsqlMigrations")), _ => throw new ArgumentException("Unsupported database provider", Enum.GetName(dbProvider)) }; } From 79118c5283fa4d0108d5fca30df2abcf4796612c Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:42:05 +0300 Subject: [PATCH 116/126] build: add ref to projects for migrations --- Backend.sln | 33 +++++++++++++++++++++++++++++++++ Endpoint/Endpoint.csproj | 3 +++ 2 files changed, 36 insertions(+) diff --git a/Backend.sln b/Backend.sln index f24d8d8..81a8dbe 100644 --- a/Backend.sln +++ b/Backend.sln @@ -29,6 +29,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "SqlData\Appl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "SqlData\Persistence\Persistence.csproj", "{48C9998C-ECE2-407F-835F-1A7255A5C99E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Migrations", "Migrations", "{79639CD4-7A16-4AB4-BBE8-672B9ACCB3F5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqliteMigrations", "SqlData\Migrations\SqliteMigrations\SqliteMigrations.csproj", "{EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7}" + ProjectSection(ProjectDependencies) = postProject + {48C9998C-ECE2-407F-835F-1A7255A5C99E} = {48C9998C-ECE2-407F-835F-1A7255A5C99E} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MysqlMigrations", "SqlData\Migrations\MysqlMigrations\MysqlMigrations.csproj", "{5861915B-9574-4D5D-872F-D54A09651697}" + ProjectSection(ProjectDependencies) = postProject + {48C9998C-ECE2-407F-835F-1A7255A5C99E} = {48C9998C-ECE2-407F-835F-1A7255A5C99E} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PsqlMigrations", "SqlData\Migrations\PsqlMigrations\PsqlMigrations.csproj", "{E9E238CD-6DD8-4B29-8C36-C61F1168FCCD}" + ProjectSection(ProjectDependencies) = postProject + {48C9998C-ECE2-407F-835F-1A7255A5C99E} = {48C9998C-ECE2-407F-835F-1A7255A5C99E} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,6 +76,18 @@ Global {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Debug|Any CPU.Build.0 = Debug|Any CPU {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Release|Any CPU.ActiveCfg = Release|Any CPU {48C9998C-ECE2-407F-835F-1A7255A5C99E}.Release|Any CPU.Build.0 = Release|Any CPU + {EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7}.Release|Any CPU.Build.0 = Release|Any CPU + {5861915B-9574-4D5D-872F-D54A09651697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5861915B-9574-4D5D-872F-D54A09651697}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5861915B-9574-4D5D-872F-D54A09651697}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5861915B-9574-4D5D-872F-D54A09651697}.Release|Any CPU.Build.0 = Release|Any CPU + {E9E238CD-6DD8-4B29-8C36-C61F1168FCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9E238CD-6DD8-4B29-8C36-C61F1168FCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9E238CD-6DD8-4B29-8C36-C61F1168FCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9E238CD-6DD8-4B29-8C36-C61F1168FCCD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +96,10 @@ Global {3BFD6180-7CA7-4E85-A379-225B872439A1} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} {0B1F3656-E5B3-440C-961F-A7D004FBE9A8} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} {48C9998C-ECE2-407F-835F-1A7255A5C99E} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} + {79639CD4-7A16-4AB4-BBE8-672B9ACCB3F5} = {7E7A63CD-547B-4FB4-A383-EB75298020A1} + {EF5530BD-4BF4-4DD8-80BB-04C6B6623DA7} = {79639CD4-7A16-4AB4-BBE8-672B9ACCB3F5} + {5861915B-9574-4D5D-872F-D54A09651697} = {79639CD4-7A16-4AB4-BBE8-672B9ACCB3F5} + {E9E238CD-6DD8-4B29-8C36-C61F1168FCCD} = {79639CD4-7A16-4AB4-BBE8-672B9ACCB3F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E80A1224-87F5-4FEB-82AE-89006BE98B12} diff --git a/Endpoint/Endpoint.csproj b/Endpoint/Endpoint.csproj index 2aad3c8..bd7ee16 100644 --- a/Endpoint/Endpoint.csproj +++ b/Endpoint/Endpoint.csproj @@ -34,6 +34,9 @@ + + + \ No newline at end of file From d2ba2d982c60197c902b9099f87928b326b4d30b Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:42:23 +0300 Subject: [PATCH 117/126] feat: add migrations --- ...0240601023106_InitialMigration.Designer.cs | 442 ++++++++++++++++++ .../20240601023106_InitialMigration.cs | 389 +++++++++++++++ .../Migrations/UberDbContextModelSnapshot.cs | 439 +++++++++++++++++ ...0240601021702_InitialMigration.Designer.cs | 442 ++++++++++++++++++ .../20240601021702_InitialMigration.cs | 365 +++++++++++++++ .../Migrations/UberDbContextModelSnapshot.cs | 439 +++++++++++++++++ ...0240601015714_InitialMigration.Designer.cs | 417 +++++++++++++++++ .../20240601015714_InitialMigration.cs | 364 +++++++++++++++ .../Migrations/UberDbContextModelSnapshot.cs | 414 ++++++++++++++++ 9 files changed, 3711 insertions(+) create mode 100644 SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.Designer.cs create mode 100644 SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.cs create mode 100644 SqlData/Migrations/MysqlMigrations/Migrations/UberDbContextModelSnapshot.cs create mode 100644 SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.Designer.cs create mode 100644 SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.cs create mode 100644 SqlData/Migrations/PsqlMigrations/Migrations/UberDbContextModelSnapshot.cs create mode 100644 SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.Designer.cs create mode 100644 SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.cs create mode 100644 SqlData/Migrations/SqliteMigrations/Migrations/UberDbContextModelSnapshot.cs diff --git a/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.Designer.cs b/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.Designer.cs new file mode 100644 index 0000000..d4c0d32 --- /dev/null +++ b/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.Designer.cs @@ -0,0 +1,442 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; + +#nullable disable + +namespace MysqlMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + [Migration("20240601023106_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BIT(1)"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BIT(1)"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.cs b/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.cs new file mode 100644 index 0000000..573b78f --- /dev/null +++ b/SqlData/Migrations/MysqlMigrations/Migrations/20240601023106_InitialMigration.cs @@ -0,0 +1,389 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MysqlMigrations.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Campus", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CodeName = table.Column(type: "TEXT", maxLength: 16, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FullName = table.Column(type: "TEXT", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Address = table.Column(type: "TEXT", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Campus", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Discipline", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Discipline", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Professor", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "TEXT", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + AltName = table.Column(type: "TEXT", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Professor", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "TypeOfOccupation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ShortName = table.Column(type: "TEXT", maxLength: 16, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_TypeOfOccupation", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Faculty", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CampusId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Faculty", x => x.Id); + table.ForeignKey( + name: "FK_Faculty_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "LectureHall", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CampusId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LectureHall", x => x.Id); + table.ForeignKey( + name: "FK_LectureHall_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Group", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FacultyId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Group", x => x.Id); + table.ForeignKey( + name: "FK_Group_Faculty_FacultyId", + column: x => x.FacultyId, + principalTable: "Faculty", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Lesson", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + IsEven = table.Column(type: "BIT(1)", nullable: false), + DayOfWeek = table.Column(type: "INTEGER", nullable: false), + PairNumber = table.Column(type: "INTEGER", nullable: false), + IsExcludedWeeks = table.Column(type: "BIT(1)", nullable: true), + GroupId = table.Column(type: "INTEGER", nullable: false), + DisciplineId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Lesson", x => x.Id); + table.ForeignKey( + name: "FK_Lesson_Discipline_DisciplineId", + column: x => x.DisciplineId, + principalTable: "Discipline", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Lesson_Group_GroupId", + column: x => x.GroupId, + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "LessonAssociation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + LinkToMeet = table.Column(type: "TEXT", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + TypeOfOccupationId = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false), + ProfessorId = table.Column(type: "INTEGER", nullable: true), + LectureHallId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LessonAssociation", x => x.Id); + table.ForeignKey( + name: "FK_LessonAssociation_LectureHall_LectureHallId", + column: x => x.LectureHallId, + principalTable: "LectureHall", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_LessonAssociation_Professor_ProfessorId", + column: x => x.ProfessorId, + principalTable: "Professor", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_TypeOfOccupation_TypeOfOccupationId", + column: x => x.TypeOfOccupationId, + principalTable: "TypeOfOccupation", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "SpecificWeek", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + WeekNumber = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SpecificWeek", x => x.Id); + table.ForeignKey( + name: "FK_SpecificWeek_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Campus_Id", + table: "Campus", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Discipline_Id", + table: "Discipline", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_CampusId", + table: "Faculty", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_Id", + table: "Faculty", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Group_FacultyId", + table: "Group", + column: "FacultyId"); + + migrationBuilder.CreateIndex( + name: "IX_Group_Id", + table: "Group", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_CampusId", + table: "LectureHall", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_Id", + table: "LectureHall", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_DisciplineId", + table: "Lesson", + column: "DisciplineId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_GroupId", + table: "Lesson", + column: "GroupId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_Id", + table: "Lesson", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_Id", + table: "LessonAssociation", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LectureHallId", + table: "LessonAssociation", + column: "LectureHallId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LessonId", + table: "LessonAssociation", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_ProfessorId", + table: "LessonAssociation", + column: "ProfessorId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_TypeOfOccupationId", + table: "LessonAssociation", + column: "TypeOfOccupationId"); + + migrationBuilder.CreateIndex( + name: "IX_Professor_Id", + table: "Professor", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_Id", + table: "SpecificWeek", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_LessonId", + table: "SpecificWeek", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_TypeOfOccupation_Id", + table: "TypeOfOccupation", + column: "Id", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LessonAssociation"); + + migrationBuilder.DropTable( + name: "SpecificWeek"); + + migrationBuilder.DropTable( + name: "LectureHall"); + + migrationBuilder.DropTable( + name: "Professor"); + + migrationBuilder.DropTable( + name: "TypeOfOccupation"); + + migrationBuilder.DropTable( + name: "Lesson"); + + migrationBuilder.DropTable( + name: "Discipline"); + + migrationBuilder.DropTable( + name: "Group"); + + migrationBuilder.DropTable( + name: "Faculty"); + + migrationBuilder.DropTable( + name: "Campus"); + } + } +} diff --git a/SqlData/Migrations/MysqlMigrations/Migrations/UberDbContextModelSnapshot.cs b/SqlData/Migrations/MysqlMigrations/Migrations/UberDbContextModelSnapshot.cs new file mode 100644 index 0000000..5e0b48f --- /dev/null +++ b/SqlData/Migrations/MysqlMigrations/Migrations/UberDbContextModelSnapshot.cs @@ -0,0 +1,439 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; + +#nullable disable + +namespace MysqlMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + partial class UberDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BIT(1)"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BIT(1)"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.Designer.cs b/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.Designer.cs new file mode 100644 index 0000000..1bea549 --- /dev/null +++ b/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.Designer.cs @@ -0,0 +1,442 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PsqlMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + [Migration("20240601021702_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BOOLEAN"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BOOLEAN"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.cs b/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.cs new file mode 100644 index 0000000..9015b9f --- /dev/null +++ b/SqlData/Migrations/PsqlMigrations/Migrations/20240601021702_InitialMigration.cs @@ -0,0 +1,365 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PsqlMigrations.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Campus", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CodeName = table.Column(type: "TEXT", maxLength: 16, nullable: false), + FullName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Address = table.Column(type: "TEXT", maxLength: 512, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Campus", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Discipline", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Discipline", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Professor", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "TEXT", nullable: false), + AltName = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Professor", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TypeOfOccupation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ShortName = table.Column(type: "TEXT", maxLength: 16, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TypeOfOccupation", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Faculty", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), + CampusId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Faculty", x => x.Id); + table.ForeignKey( + name: "FK_Faculty_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "LectureHall", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false), + CampusId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LectureHall", x => x.Id); + table.ForeignKey( + name: "FK_LectureHall_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Group", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false), + FacultyId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Group", x => x.Id); + table.ForeignKey( + name: "FK_Group_Faculty_FacultyId", + column: x => x.FacultyId, + principalTable: "Faculty", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Lesson", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + IsEven = table.Column(type: "BOOLEAN", nullable: false), + DayOfWeek = table.Column(type: "INTEGER", nullable: false), + PairNumber = table.Column(type: "INTEGER", nullable: false), + IsExcludedWeeks = table.Column(type: "BOOLEAN", nullable: true), + GroupId = table.Column(type: "INTEGER", nullable: false), + DisciplineId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Lesson", x => x.Id); + table.ForeignKey( + name: "FK_Lesson_Discipline_DisciplineId", + column: x => x.DisciplineId, + principalTable: "Discipline", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Lesson_Group_GroupId", + column: x => x.GroupId, + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "LessonAssociation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + LinkToMeet = table.Column(type: "TEXT", maxLength: 512, nullable: true), + TypeOfOccupationId = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false), + ProfessorId = table.Column(type: "INTEGER", nullable: true), + LectureHallId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LessonAssociation", x => x.Id); + table.ForeignKey( + name: "FK_LessonAssociation_LectureHall_LectureHallId", + column: x => x.LectureHallId, + principalTable: "LectureHall", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_LessonAssociation_Professor_ProfessorId", + column: x => x.ProfessorId, + principalTable: "Professor", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_TypeOfOccupation_TypeOfOccupationId", + column: x => x.TypeOfOccupationId, + principalTable: "TypeOfOccupation", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SpecificWeek", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + WeekNumber = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SpecificWeek", x => x.Id); + table.ForeignKey( + name: "FK_SpecificWeek_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Campus_Id", + table: "Campus", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Discipline_Id", + table: "Discipline", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_CampusId", + table: "Faculty", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_Id", + table: "Faculty", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Group_FacultyId", + table: "Group", + column: "FacultyId"); + + migrationBuilder.CreateIndex( + name: "IX_Group_Id", + table: "Group", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_CampusId", + table: "LectureHall", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_Id", + table: "LectureHall", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_DisciplineId", + table: "Lesson", + column: "DisciplineId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_GroupId", + table: "Lesson", + column: "GroupId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_Id", + table: "Lesson", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_Id", + table: "LessonAssociation", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LectureHallId", + table: "LessonAssociation", + column: "LectureHallId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LessonId", + table: "LessonAssociation", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_ProfessorId", + table: "LessonAssociation", + column: "ProfessorId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_TypeOfOccupationId", + table: "LessonAssociation", + column: "TypeOfOccupationId"); + + migrationBuilder.CreateIndex( + name: "IX_Professor_Id", + table: "Professor", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_Id", + table: "SpecificWeek", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_LessonId", + table: "SpecificWeek", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_TypeOfOccupation_Id", + table: "TypeOfOccupation", + column: "Id", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LessonAssociation"); + + migrationBuilder.DropTable( + name: "SpecificWeek"); + + migrationBuilder.DropTable( + name: "LectureHall"); + + migrationBuilder.DropTable( + name: "Professor"); + + migrationBuilder.DropTable( + name: "TypeOfOccupation"); + + migrationBuilder.DropTable( + name: "Lesson"); + + migrationBuilder.DropTable( + name: "Discipline"); + + migrationBuilder.DropTable( + name: "Group"); + + migrationBuilder.DropTable( + name: "Faculty"); + + migrationBuilder.DropTable( + name: "Campus"); + } + } +} diff --git a/SqlData/Migrations/PsqlMigrations/Migrations/UberDbContextModelSnapshot.cs b/SqlData/Migrations/PsqlMigrations/Migrations/UberDbContextModelSnapshot.cs new file mode 100644 index 0000000..6039a03 --- /dev/null +++ b/SqlData/Migrations/PsqlMigrations/Migrations/UberDbContextModelSnapshot.cs @@ -0,0 +1,439 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PsqlMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + partial class UberDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BOOLEAN"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BOOLEAN"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.Designer.cs b/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.Designer.cs new file mode 100644 index 0000000..e100297 --- /dev/null +++ b/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.Designer.cs @@ -0,0 +1,417 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; + +#nullable disable + +namespace SqliteMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + [Migration("20240601015714_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BOOLEAN"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BOOLEAN"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.cs b/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.cs new file mode 100644 index 0000000..9afa7f2 --- /dev/null +++ b/SqlData/Migrations/SqliteMigrations/Migrations/20240601015714_InitialMigration.cs @@ -0,0 +1,364 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SqliteMigrations.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Campus", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CodeName = table.Column(type: "TEXT", maxLength: 16, nullable: false), + FullName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Address = table.Column(type: "TEXT", maxLength: 512, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Campus", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Discipline", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Discipline", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Professor", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + AltName = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Professor", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TypeOfOccupation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ShortName = table.Column(type: "TEXT", maxLength: 16, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TypeOfOccupation", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Faculty", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), + CampusId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Faculty", x => x.Id); + table.ForeignKey( + name: "FK_Faculty_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "LectureHall", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false), + CampusId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LectureHall", x => x.Id); + table.ForeignKey( + name: "FK_LectureHall_Campus_CampusId", + column: x => x.CampusId, + principalTable: "Campus", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Group", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", maxLength: 64, nullable: false), + FacultyId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Group", x => x.Id); + table.ForeignKey( + name: "FK_Group_Faculty_FacultyId", + column: x => x.FacultyId, + principalTable: "Faculty", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Lesson", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + IsEven = table.Column(type: "BOOLEAN", nullable: false), + DayOfWeek = table.Column(type: "INTEGER", nullable: false), + PairNumber = table.Column(type: "INTEGER", nullable: false), + IsExcludedWeeks = table.Column(type: "BOOLEAN", nullable: true), + GroupId = table.Column(type: "INTEGER", nullable: false), + DisciplineId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Lesson", x => x.Id); + table.ForeignKey( + name: "FK_Lesson_Discipline_DisciplineId", + column: x => x.DisciplineId, + principalTable: "Discipline", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Lesson_Group_GroupId", + column: x => x.GroupId, + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "LessonAssociation", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + LinkToMeet = table.Column(type: "TEXT", maxLength: 512, nullable: true), + TypeOfOccupationId = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false), + ProfessorId = table.Column(type: "INTEGER", nullable: true), + LectureHallId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LessonAssociation", x => x.Id); + table.ForeignKey( + name: "FK_LessonAssociation_LectureHall_LectureHallId", + column: x => x.LectureHallId, + principalTable: "LectureHall", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_LessonAssociation_Professor_ProfessorId", + column: x => x.ProfessorId, + principalTable: "Professor", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_LessonAssociation_TypeOfOccupation_TypeOfOccupationId", + column: x => x.TypeOfOccupationId, + principalTable: "TypeOfOccupation", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SpecificWeek", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + WeekNumber = table.Column(type: "INTEGER", nullable: false), + LessonId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SpecificWeek", x => x.Id); + table.ForeignKey( + name: "FK_SpecificWeek_Lesson_LessonId", + column: x => x.LessonId, + principalTable: "Lesson", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Campus_Id", + table: "Campus", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Discipline_Id", + table: "Discipline", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_CampusId", + table: "Faculty", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_Faculty_Id", + table: "Faculty", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Group_FacultyId", + table: "Group", + column: "FacultyId"); + + migrationBuilder.CreateIndex( + name: "IX_Group_Id", + table: "Group", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_CampusId", + table: "LectureHall", + column: "CampusId"); + + migrationBuilder.CreateIndex( + name: "IX_LectureHall_Id", + table: "LectureHall", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_DisciplineId", + table: "Lesson", + column: "DisciplineId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_GroupId", + table: "Lesson", + column: "GroupId"); + + migrationBuilder.CreateIndex( + name: "IX_Lesson_Id", + table: "Lesson", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_Id", + table: "LessonAssociation", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LectureHallId", + table: "LessonAssociation", + column: "LectureHallId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_LessonId", + table: "LessonAssociation", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_ProfessorId", + table: "LessonAssociation", + column: "ProfessorId"); + + migrationBuilder.CreateIndex( + name: "IX_LessonAssociation_TypeOfOccupationId", + table: "LessonAssociation", + column: "TypeOfOccupationId"); + + migrationBuilder.CreateIndex( + name: "IX_Professor_Id", + table: "Professor", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_Id", + table: "SpecificWeek", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_SpecificWeek_LessonId", + table: "SpecificWeek", + column: "LessonId"); + + migrationBuilder.CreateIndex( + name: "IX_TypeOfOccupation_Id", + table: "TypeOfOccupation", + column: "Id", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LessonAssociation"); + + migrationBuilder.DropTable( + name: "SpecificWeek"); + + migrationBuilder.DropTable( + name: "LectureHall"); + + migrationBuilder.DropTable( + name: "Professor"); + + migrationBuilder.DropTable( + name: "TypeOfOccupation"); + + migrationBuilder.DropTable( + name: "Lesson"); + + migrationBuilder.DropTable( + name: "Discipline"); + + migrationBuilder.DropTable( + name: "Group"); + + migrationBuilder.DropTable( + name: "Faculty"); + + migrationBuilder.DropTable( + name: "Campus"); + } + } +} diff --git a/SqlData/Migrations/SqliteMigrations/Migrations/UberDbContextModelSnapshot.cs b/SqlData/Migrations/SqliteMigrations/Migrations/UberDbContextModelSnapshot.cs new file mode 100644 index 0000000..c463963 --- /dev/null +++ b/SqlData/Migrations/SqliteMigrations/Migrations/UberDbContextModelSnapshot.cs @@ -0,0 +1,414 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mirea.Api.DataAccess.Persistence; + +#nullable disable + +namespace SqliteMigrations.Migrations +{ + [DbContext(typeof(UberDbContext))] + partial class UberDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("CodeName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Campus", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Discipline", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Faculty", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FacultyId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FacultyId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampusId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("LectureHall", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("DisciplineId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("IsEven") + .HasColumnType("BOOLEAN"); + + b.Property("IsExcludedWeeks") + .HasColumnType("BOOLEAN"); + + b.Property("PairNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisciplineId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Lesson", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LectureHallId") + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("LinkToMeet") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("ProfessorId") + .HasColumnType("INTEGER"); + + b.Property("TypeOfOccupationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LectureHallId"); + + b.HasIndex("LessonId"); + + b.HasIndex("ProfessorId"); + + b.HasIndex("TypeOfOccupationId"); + + b.ToTable("LessonAssociation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AltName") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("Professor", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LessonId") + .HasColumnType("INTEGER"); + + b.Property("WeekNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("LessonId"); + + b.ToTable("SpecificWeek", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("TypeOfOccupation", (string)null); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("Faculties") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Faculty", "Faculty") + .WithMany("Groups") + .HasForeignKey("FacultyId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Faculty"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Campus", "Campus") + .WithMany("LectureHalls") + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Discipline", "Discipline") + .WithMany("Lessons") + .HasForeignKey("DisciplineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Group", "Group") + .WithMany("Lessons") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Discipline"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LessonAssociation", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", "LectureHall") + .WithMany("LessonAssociations") + .HasForeignKey("LectureHallId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("LessonAssociations") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Professor", "Professor") + .WithMany("LessonAssociations") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", "TypeOfOccupation") + .WithMany("Lessons") + .HasForeignKey("TypeOfOccupationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LectureHall"); + + b.Navigation("Lesson"); + + b.Navigation("Professor"); + + b.Navigation("TypeOfOccupation"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.SpecificWeek", b => + { + b.HasOne("Mirea.Api.DataAccess.Domain.Schedule.Lesson", "Lesson") + .WithMany("SpecificWeeks") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Campus", b => + { + b.Navigation("Faculties"); + + b.Navigation("LectureHalls"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Discipline", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Faculty", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Group", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.LectureHall", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Lesson", b => + { + b.Navigation("LessonAssociations"); + + b.Navigation("SpecificWeeks"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.Professor", b => + { + b.Navigation("LessonAssociations"); + }); + + modelBuilder.Entity("Mirea.Api.DataAccess.Domain.Schedule.TypeOfOccupation", b => + { + b.Navigation("Lessons"); + }); +#pragma warning restore 612, 618 + } + } +} From 827cdaf9f9b8544493f4e694cf28dce1c9d1289a Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 05:43:00 +0300 Subject: [PATCH 118/126] refactor: change create database to migrate --- SqlData/Persistence/DbInitializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SqlData/Persistence/DbInitializer.cs b/SqlData/Persistence/DbInitializer.cs index 87f3a9b..7bedd91 100644 --- a/SqlData/Persistence/DbInitializer.cs +++ b/SqlData/Persistence/DbInitializer.cs @@ -6,6 +6,6 @@ public static class DbInitializer { public static void Initialize(DbContext dbContext) { - dbContext.Database.EnsureCreated(); + dbContext.Database.Migrate(); } } \ No newline at end of file From d09011d25acea1eff021e98c9af35ae273b24f78 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 06:27:49 +0300 Subject: [PATCH 119/126] feat: add create admin --- .../Controllers/Configuration/SetupController.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 7abd5ee..ac55b56 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -28,7 +28,9 @@ namespace Mirea.Api.Endpoint.Controllers.Configuration; [MaintenanceModeIgnore] public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService, IMemoryCache cache) : BaseController { - private const string CacheGeneralKey = "config_part"; + private const string CacheGeneralKey = "config_general"; + private const string CacheAdminKey = "config_admin"; + private GeneralConfig GeneralConfig { get => cache.Get(CacheGeneralKey) ?? new GeneralConfig(); @@ -190,6 +192,16 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return Ok(true); } + [HttpPost("CreateAdmin")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult CreateAdmin([FromBody] CreateUserRequest user) + { + // todo: change CreateUserRequest to Domain entity + cache.Set(CacheAdminKey, user); + return Ok(true); + } + [HttpPost("SetLogging")] [TokenAuthentication] [BadRequestResponse] From 1fd6c8657a0f3a1a1413297d6017eac40fa8287f Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 06:28:13 +0300 Subject: [PATCH 120/126] fix: change connction string for mysql --- Endpoint/Controllers/Configuration/SetupController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index ac55b56..1e9bacd 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -114,11 +114,11 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur [BadRequestResponse] public ActionResult SetMysql([FromBody] DatabaseRequest request) { - string connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database}"; + string connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database};"; if (request.Password != null) - connectionString += $";Pwd={request.Password}"; + connectionString += $"Pwd={request.Password};"; if (request.Ssl) - connectionString += ";SslMode=Require;"; + connectionString += "SslMode=Require;"; return SetDatabase(connectionString, DbSettings.DatabaseEnum.Mysql); } From 5400e0c8737704f04de2afd03c28bbe520266a90 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 06:29:16 +0300 Subject: [PATCH 121/126] feat: create submit configuration --- .../Configuration/SetupController.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 1e9bacd..bc96cb1 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -11,6 +11,7 @@ using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Settings; +using Mirea.Api.Endpoint.Configuration.General.Validators; using MySqlConnector; using Npgsql; using StackExchange.Redis; @@ -32,7 +33,7 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur private const string CacheAdminKey = "config_admin"; private GeneralConfig GeneralConfig -{ + { get => cache.Get(CacheGeneralKey) ?? new GeneralConfig(); set => cache.Set(CacheGeneralKey, value); } @@ -87,8 +88,8 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur }; GeneralConfig = general; - return Ok(true); - } + return Ok(true); + } catch (TException ex) { throw new ControllerArgumentException($"Error when connecting: {ex.Message}"); @@ -278,5 +279,31 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur return true; } + [HttpPost("Submit")] + [TokenAuthentication] + [BadRequestResponse] + public ActionResult Submit() + { + if (!new SettingsRequiredValidator(GeneralConfig).AreSettingsValid()) + throw new ControllerArgumentException("The necessary data has not been configured."); + // todo: change CreateUserRequest to Domain entity + if (!cache.TryGetValue(CacheAdminKey, out CreateUserRequest? user) || user == null) + throw new ControllerArgumentException("The administrator's data was not set."); + + if (System.IO.File.Exists(PathBuilder.Combine(GeneralConfig.FilePath))) + System.IO.File.Delete(PathBuilder.Combine(GeneralConfig.FilePath)); + + System.IO.File.WriteAllText(PathBuilder.Combine("admin.json"), JsonSerializer.Serialize(user)); + + System.IO.File.WriteAllText( + PathBuilder.Combine(GeneralConfig.FilePath), + JsonSerializer.Serialize(GeneralConfig, new JsonSerializerOptions + { + WriteIndented = true + }) + ); + + return true; + } } \ No newline at end of file From fdf0ecc9ef43bb52c5237350668500074bbef501 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 07:25:51 +0300 Subject: [PATCH 122/126] feat: add is default path --- Endpoint/Common/Services/PathBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Endpoint/Common/Services/PathBuilder.cs b/Endpoint/Common/Services/PathBuilder.cs index 7df60d8..799f582 100644 --- a/Endpoint/Common/Services/PathBuilder.cs +++ b/Endpoint/Common/Services/PathBuilder.cs @@ -6,6 +6,7 @@ namespace Mirea.Api.Endpoint.Common.Services; public static class PathBuilder { + public static bool IsDefaultPath => Environment.GetEnvironmentVariable("PATH_TO_SAVE") == null; public static string PathToSave => Environment.GetEnvironmentVariable("PATH_TO_SAVE") ?? Directory.GetCurrentDirectory(); public static string Combine(params string[] paths) => Path.Combine([.. paths.Prepend(PathToSave)]); } \ No newline at end of file From 32621515db59776b2bb20d46731eeab960a3e553 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 07:26:22 +0300 Subject: [PATCH 123/126] fix: default value is null for optional body --- Endpoint/Controllers/Configuration/SetupController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index bc96cb1..de0a0be 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -206,7 +206,7 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur [HttpPost("SetLogging")] [TokenAuthentication] [BadRequestResponse] - public ActionResult SetLogging([FromBody] LoggingRequest? request) + public ActionResult SetLogging([FromBody] LoggingRequest? request = null) { var settings = (request == null) switch { @@ -234,7 +234,7 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur [HttpPost("SetEmail")] [TokenAuthentication] [BadRequestResponse] - public ActionResult SetEmail([FromBody] EmailRequest? request) + public ActionResult SetEmail([FromBody] EmailRequest? request = null) { var settings = (request == null) switch { From ded577f40acb67424b150fbf35cb117ec1b75d0e Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 07:27:18 +0300 Subject: [PATCH 124/126] feat: add path depending on OS --- Endpoint/Controllers/Configuration/SetupController.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index de0a0be..5d98699 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -213,8 +213,10 @@ public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigur true => new LogSettings { EnableLogToFile = true, - LogFileName = "logging-", - LogFilePath = "logs" + LogFileName = "log-", + LogFilePath = OperatingSystem.IsWindows() || PathBuilder.IsDefaultPath ? + PathBuilder.Combine("logs") : + "/var/log/mirea" }, false => new LogSettings { From e088374b1450c7ba27e1306fae734d7127d5490b Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 07:31:14 +0300 Subject: [PATCH 125/126] feat: add request for create user --- ApiDto/Requests/CreateUserRequest.cs | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 ApiDto/Requests/CreateUserRequest.cs diff --git a/ApiDto/Requests/CreateUserRequest.cs b/ApiDto/Requests/CreateUserRequest.cs new file mode 100644 index 0000000..3bcf70c --- /dev/null +++ b/ApiDto/Requests/CreateUserRequest.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mirea.Api.Dto.Requests; + +/// +/// Request model for creating a user. +/// +public class CreateUserRequest +{ + /// + /// Gets or sets the email address of the user. + /// + /// + /// The email address is a required field. + /// + [Required] + public required string Email { get; set; } + + /// + /// Gets or sets the username of the user. + /// + /// + /// The username is a required field. + /// + [Required] + public required string Username { get; set; } + + /// + /// Gets or sets the password of the user. + /// + /// + /// The password is a required field. + /// + [Required] + public required string Password { get; set; } +} From 63216f3b66ad1af077fa771a7e0966f04f404d99 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 1 Jun 2024 07:33:08 +0300 Subject: [PATCH 126/126] feat: comment this for show controller in swagger --- Endpoint/Controllers/Configuration/SetupController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Endpoint/Controllers/Configuration/SetupController.cs b/Endpoint/Controllers/Configuration/SetupController.cs index 5d98699..1c9d275 100644 --- a/Endpoint/Controllers/Configuration/SetupController.cs +++ b/Endpoint/Controllers/Configuration/SetupController.cs @@ -27,6 +27,7 @@ namespace Mirea.Api.Endpoint.Controllers.Configuration; [ApiVersion("1.0")] [ApiController] [MaintenanceModeIgnore] +[ApiExplorerSettings(IgnoreApi = true)] public class SetupController(ISetupToken setupToken, IMaintenanceModeNotConfigureService notConfigureService, IMemoryCache cache) : BaseController { private const string CacheGeneralKey = "config_general";