Release v1.0.0 #16
.envDbInitializer.csDependencyInjection.csnuget.config
.gitea/workflows
.gitignoreApiDto
ApiDto.csproj
Backend.slnDockerfileCommon
AuthRoles.csCacheType.csDatabaseType.csOAuthProvider.csPairPeriodTime.csPasswordPolicy.csTwoFactorAuthentication.cs
Requests
Responses
AvailableOAuthProvidersResponse.csCampusBasicInfoResponse.csCampusDetailsResponse.cs
Configuration
DisciplineResponse.csErrorResponse.csFacultyResponse.csGroupDetailsResponse.csGroupResponse.csLectureHallDetailsResponse.csLectureHallResponse.csProfessorResponse.csScheduleResponse.csTotpKeyResponse.csUserResponse.csEndpoint
Backend.httpISaveSettings.cs
README.mdCommon
Attributes
BadRequestResponseAttribute.csCacheMaxAgeAttribute.csLocalhostAttribute.csMaintenanceModeIgnoreAttribute.csNotFoundResponseAttribute.csSwaggerDefaultAttribute.csTokenAuthenticationAttribute.cs
Exceptions
Interfaces
MapperDto
AvailableProvidersConverter.csPairPeriodTimeConverter.csPasswordPolicyConverter.csTwoFactorAuthenticationConverter.cs
Services
Configuration
Core
BackgroundTasks
Middleware
CacheMaxAgeMiddleware.csCookieAuthorizationMiddleware.csCustomExceptionHandlerMiddleware.csJwtRevocationMiddleware.csMaintenanceModeMiddleware.cs
Startup
Model
SwaggerOptions
Validation
Controllers
BaseController.cs
Endpoint.csprojProgram.csConfiguration
V1
AuthController.csCampusController.csDisciplineController.csFacultyController.csGroupController.csImportController.csLectureHallController.csProfessorController.csScheduleController.csSecurityController.cs
WeatherForecastController.csSync
WeatherForecast.cswwwroot
css
swagger
Security
Common
CookieNames.cs
DependencyInjection.csDomain
Caching
CookieOptionsParameters.csOAuth2
OAuthProvider.csOAuthUser.csPasswordPolicy.csRequestContextInfo.csTwoFactorAuthenticator.csUser.csInterfaces
Properties
Security.csprojServices
SqlData
Application
Application.csprojDependencyInjection.cs
Common
Cqrs
Campus
Queries
Discipline
Queries
Faculty
Queries
Group
Queries
LectureHall
Queries
Professor
Queries
GetProfessorDetails
GetProfessorDetailsBySearch
GetProfessorList
Schedule
Interfaces
Domain
Domain.csproj
Schedule
Migrations
MysqlMigrations
Migrations
20240601023106_InitialMigration.Designer.cs20240601023106_InitialMigration.cs20241027034820_RemoveUnusedRef.Designer.cs20241027034820_RemoveUnusedRef.csUberDbContextModelSnapshot.cs
MysqlMigrations.csprojPsqlMigrations
Migrations
20240601021702_InitialMigration.Designer.cs20240601021702_InitialMigration.cs20241027032753_RemoveUnusedRef.Designer.cs20241027032753_RemoveUnusedRef.csUberDbContextModelSnapshot.cs
PsqlMigrations.csprojSqliteMigrations
Persistence
Common
BaseDbContext.csConfigurationResolver.csDatabaseProvider.csDbContextFactory.csModelBuilderExtensions.cs
Contexts
Schedule
EntityTypeConfigurations
Persistence.csprojUberDbContext.cs
17
ApiDto/Common/CacheType.cs
Normal file
17
ApiDto/Common/CacheType.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Mirea.Api.Dto.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the types of caching mechanisms available.
|
||||||
|
/// </summary>
|
||||||
|
public enum CacheType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Memcached caching type.
|
||||||
|
/// </summary>
|
||||||
|
Memcached,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redis caching type.
|
||||||
|
/// </summary>
|
||||||
|
Redis
|
||||||
|
}
|
22
ApiDto/Common/DatabaseType.cs
Normal file
22
ApiDto/Common/DatabaseType.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace Mirea.Api.Dto.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the types of databases supported.
|
||||||
|
/// </summary>
|
||||||
|
public enum DatabaseType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MySQL database type.
|
||||||
|
/// </summary>
|
||||||
|
Mysql,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SQLite database type.
|
||||||
|
/// </summary>
|
||||||
|
Sqlite,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PostgreSQL database type.
|
||||||
|
/// </summary>
|
||||||
|
PostgresSql
|
||||||
|
}
|
@ -10,27 +10,18 @@ public class CreateUserRequest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the email address of the user.
|
/// Gets or sets the email address of the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// The email address is a required field.
|
|
||||||
/// </remarks>
|
|
||||||
[Required]
|
[Required]
|
||||||
public required string Email { get; set; }
|
public required string Email { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the username of the user.
|
/// Gets or sets the username of the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// The username is a required field.
|
|
||||||
/// </remarks>
|
|
||||||
[Required]
|
[Required]
|
||||||
public required string Username { get; set; }
|
public required string Username { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the password of the user.
|
/// Gets or sets the password of the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// The password is a required field.
|
|
||||||
/// </remarks>
|
|
||||||
[Required]
|
[Required]
|
||||||
public required string Password { get; set; }
|
public required string Password { get; set; }
|
||||||
}
|
}
|
||||||
|
10
ApiDto/Responses/AvailableProvidersResponse.cs → ApiDto/Responses/AvailableOAuthProvidersResponse.cs
10
ApiDto/Responses/AvailableProvidersResponse.cs → ApiDto/Responses/AvailableOAuthProvidersResponse.cs
@ -1,16 +1,17 @@
|
|||||||
using Mirea.Api.Dto.Common;
|
using Mirea.Api.Dto.Common;
|
||||||
using System;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Mirea.Api.Dto.Responses;
|
namespace Mirea.Api.Dto.Responses;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the response containing information about available OAuth providers.
|
/// Represents the response containing information about available OAuth providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AvailableProvidersResponse
|
public class AvailableOAuthProvidersResponse
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the OAuth provider.
|
/// Gets or sets the name of the OAuth provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Required]
|
||||||
public required string ProviderName { get; set; }
|
public required string ProviderName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -19,7 +20,8 @@ public class AvailableProvidersResponse
|
|||||||
public OAuthProvider Provider { get; set; }
|
public OAuthProvider Provider { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the redirect URI for the OAuth provider.
|
/// Gets or sets the redirect URL for the OAuth provider's authorization process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required Uri Redirect { get; set; }
|
[Required]
|
||||||
|
public required string Redirect { get; set; }
|
||||||
}
|
}
|
29
ApiDto/Responses/Configuration/CacheResponse.cs
Normal file
29
ApiDto/Responses/Configuration/CacheResponse.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using Mirea.Api.Dto.Common;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Dto.Responses.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a response containing cache configuration details.
|
||||||
|
/// </summary>
|
||||||
|
public class CacheResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type of cache database.
|
||||||
|
/// </summary>
|
||||||
|
public CacheType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the server address.
|
||||||
|
/// </summary>
|
||||||
|
public string? Server { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the port number.
|
||||||
|
/// </summary>
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the password.
|
||||||
|
/// </summary>
|
||||||
|
public string? Password { get; set; }
|
||||||
|
}
|
49
ApiDto/Responses/Configuration/DatabaseResponse.cs
Normal file
49
ApiDto/Responses/Configuration/DatabaseResponse.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Mirea.Api.Dto.Common;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Dto.Responses.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a response containing database configuration details.
|
||||||
|
/// </summary>
|
||||||
|
public class DatabaseResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type of database.
|
||||||
|
/// </summary>
|
||||||
|
public DatabaseType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the server address.
|
||||||
|
/// </summary>
|
||||||
|
public string? Server { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the port number.
|
||||||
|
/// </summary>
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the database name.
|
||||||
|
/// </summary>
|
||||||
|
public string? Database { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the username.
|
||||||
|
/// </summary>
|
||||||
|
public string? User { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether SSL is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool Ssl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the password.
|
||||||
|
/// </summary>
|
||||||
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the path to database. Only for Sqlite
|
||||||
|
/// </summary>
|
||||||
|
public string? PathToDatabase { get; set; }
|
||||||
|
}
|
17
ApiDto/Responses/TotpKeyResponse.cs
Normal file
17
ApiDto/Responses/TotpKeyResponse.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Mirea.Api.Dto.Responses;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the response containing the TOTP (Time-Based One-Time Password) key details.
|
||||||
|
/// </summary>
|
||||||
|
public class TotpKeyResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the secret key used for TOTP generation.
|
||||||
|
/// </summary>
|
||||||
|
public required string Secret { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image (QR code) representing the TOTP key.
|
||||||
|
/// </summary>
|
||||||
|
public required string Image { get; set; }
|
||||||
|
}
|
35
ApiDto/Responses/UserResponse.cs
Normal file
35
ApiDto/Responses/UserResponse.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Mirea.Api.Dto.Common;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Dto.Responses;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a response containing user information.
|
||||||
|
/// </summary>
|
||||||
|
public class UserResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the email address of the user.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public required string Email { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the username of the user.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public required string Username { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the user has two-factor authentication enabled.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public bool TwoFactorAuthenticatorEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a collection of OAuth providers used by the user.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public required IEnumerable<OAuthProvider> UsedOAuthProviders { get; set; }
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Mirea.Api.Dto.Common;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model;
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
|
||||||
|
|
||||||
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
|
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ public static class CacheConfiguration
|
|||||||
public static IServiceCollection AddCustomRedis(this IServiceCollection services, IConfiguration configuration, IHealthChecksBuilder? healthChecksBuilder = null)
|
public static IServiceCollection AddCustomRedis(this IServiceCollection services, IConfiguration configuration, IHealthChecksBuilder? healthChecksBuilder = null)
|
||||||
{
|
{
|
||||||
var cache = configuration.Get<GeneralConfig>()?.CacheSettings;
|
var cache = configuration.Get<GeneralConfig>()?.CacheSettings;
|
||||||
if (cache?.TypeDatabase != CacheSettings.CacheEnum.Redis)
|
if (cache?.TypeDatabase != CacheType.Redis)
|
||||||
return services;
|
return services;
|
||||||
|
|
||||||
services.AddStackExchangeRedisCache(options =>
|
services.AddStackExchangeRedisCache(options =>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Mirea.Api.Dto.Common;
|
||||||
using Mirea.Api.Endpoint.Common.Services.Security;
|
using Mirea.Api.Endpoint.Common.Services.Security;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model;
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
|
||||||
using Mirea.Api.Security;
|
using Mirea.Api.Security;
|
||||||
using Mirea.Api.Security.Common.Interfaces;
|
using Mirea.Api.Security.Common.Interfaces;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ public static class SecureConfiguration
|
|||||||
|
|
||||||
services.AddSingleton<IRevokedToken, MemoryRevokedTokenService>();
|
services.AddSingleton<IRevokedToken, MemoryRevokedTokenService>();
|
||||||
|
|
||||||
if (configuration.Get<GeneralConfig>()?.CacheSettings?.TypeDatabase == CacheSettings.CacheEnum.Redis)
|
if (configuration.Get<GeneralConfig>()?.CacheSettings?.TypeDatabase == CacheType.Redis)
|
||||||
services.AddSingleton<ICacheService, DistributedCacheService>();
|
services.AddSingleton<ICacheService, DistributedCacheService>();
|
||||||
else
|
else
|
||||||
services.AddSingleton<ICacheService, MemoryCacheService>();
|
services.AddSingleton<ICacheService, MemoryCacheService>();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
|
using Mirea.Api.Dto.Common;
|
||||||
|
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
|
||||||
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
|
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
|
||||||
|
|
||||||
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
||||||
@ -6,18 +7,12 @@ namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
|||||||
[RequiredSettings]
|
[RequiredSettings]
|
||||||
public class CacheSettings : IIsConfigured
|
public class CacheSettings : IIsConfigured
|
||||||
{
|
{
|
||||||
public enum CacheEnum
|
public CacheType TypeDatabase { get; set; }
|
||||||
{
|
|
||||||
Memcached,
|
|
||||||
Redis
|
|
||||||
}
|
|
||||||
|
|
||||||
public CacheEnum TypeDatabase { get; set; }
|
|
||||||
public string? ConnectionString { get; set; }
|
public string? ConnectionString { get; set; }
|
||||||
|
|
||||||
public bool IsConfigured()
|
public bool IsConfigured()
|
||||||
{
|
{
|
||||||
return TypeDatabase == CacheEnum.Memcached ||
|
return TypeDatabase == CacheType.Memcached ||
|
||||||
!string.IsNullOrEmpty(ConnectionString);
|
!string.IsNullOrEmpty(ConnectionString);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Mirea.Api.DataAccess.Persistence.Common;
|
using Mirea.Api.DataAccess.Persistence.Common;
|
||||||
|
using Mirea.Api.Dto.Common;
|
||||||
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
|
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
|
||||||
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
|
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
@ -9,22 +10,16 @@ namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
|||||||
[RequiredSettings]
|
[RequiredSettings]
|
||||||
public class DbSettings : IIsConfigured
|
public class DbSettings : IIsConfigured
|
||||||
{
|
{
|
||||||
public enum DatabaseEnum
|
public DatabaseType TypeDatabase { get; set; }
|
||||||
{
|
|
||||||
Mysql,
|
|
||||||
Sqlite,
|
|
||||||
PostgresSql
|
|
||||||
}
|
|
||||||
public DatabaseEnum TypeDatabase { get; set; }
|
|
||||||
public required string ConnectionStringSql { get; set; }
|
public required string ConnectionStringSql { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public DatabaseProvider DatabaseProvider =>
|
public DatabaseProvider DatabaseProvider =>
|
||||||
TypeDatabase switch
|
TypeDatabase switch
|
||||||
{
|
{
|
||||||
DatabaseEnum.PostgresSql => DatabaseProvider.Postgresql,
|
DatabaseType.PostgresSql => DatabaseProvider.Postgresql,
|
||||||
DatabaseEnum.Mysql => DatabaseProvider.Mysql,
|
DatabaseType.Mysql => DatabaseProvider.Mysql,
|
||||||
DatabaseEnum.Sqlite => DatabaseProvider.Sqlite,
|
DatabaseType.Sqlite => DatabaseProvider.Sqlite,
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,15 +4,21 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Mirea.Api.Dto.Common;
|
||||||
using Mirea.Api.Dto.Requests;
|
using Mirea.Api.Dto.Requests;
|
||||||
using Mirea.Api.Dto.Requests.Configuration;
|
using Mirea.Api.Dto.Requests.Configuration;
|
||||||
|
using Mirea.Api.Dto.Responses;
|
||||||
|
using Mirea.Api.Dto.Responses.Configuration;
|
||||||
using Mirea.Api.Endpoint.Common.Attributes;
|
using Mirea.Api.Endpoint.Common.Attributes;
|
||||||
using Mirea.Api.Endpoint.Common.Exceptions;
|
using Mirea.Api.Endpoint.Common.Exceptions;
|
||||||
using Mirea.Api.Endpoint.Common.Interfaces;
|
using Mirea.Api.Endpoint.Common.Interfaces;
|
||||||
|
using Mirea.Api.Endpoint.Common.MapperDto;
|
||||||
using Mirea.Api.Endpoint.Common.Services;
|
using Mirea.Api.Endpoint.Common.Services;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model;
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
||||||
using Mirea.Api.Endpoint.Configuration.Validation.Validators;
|
using Mirea.Api.Endpoint.Configuration.Validation.Validators;
|
||||||
|
using Mirea.Api.Security.Common.Domain;
|
||||||
using Mirea.Api.Security.Services;
|
using Mirea.Api.Security.Services;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
@ -36,7 +42,8 @@ public class SetupController(
|
|||||||
ISetupToken setupToken,
|
ISetupToken setupToken,
|
||||||
IMaintenanceModeNotConfigureService notConfigureService,
|
IMaintenanceModeNotConfigureService notConfigureService,
|
||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
PasswordHashService passwordHashService) : BaseController
|
PasswordHashService passwordHashService,
|
||||||
|
IOptionsSnapshot<Admin> user) : BaseController
|
||||||
{
|
{
|
||||||
private const string CacheGeneralKey = "config_general";
|
private const string CacheGeneralKey = "config_general";
|
||||||
private const string CacheAdminKey = "config_admin";
|
private const string CacheAdminKey = "config_admin";
|
||||||
@ -95,7 +102,12 @@ public class SetupController(
|
|||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionResult<bool> SetDatabase<TConnection, TException>(string connectionString, DbSettings.DatabaseEnum databaseType)
|
[HttpGet("IsConfiguredToken")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
public ActionResult<bool> IsConfiguredToken() =>
|
||||||
|
Ok(true);
|
||||||
|
|
||||||
|
private void SetDatabase<TConnection, TException>(string connectionString, DatabaseType databaseType)
|
||||||
where TConnection : class, IDbConnection, new()
|
where TConnection : class, IDbConnection, new()
|
||||||
where TException : Exception
|
where TException : Exception
|
||||||
{
|
{
|
||||||
@ -118,8 +130,6 @@ public class SetupController(
|
|||||||
TypeDatabase = databaseType
|
TypeDatabase = databaseType
|
||||||
};
|
};
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
return Ok(true);
|
|
||||||
}
|
}
|
||||||
catch (TException ex)
|
catch (TException ex)
|
||||||
{
|
{
|
||||||
@ -138,7 +148,20 @@ public class SetupController(
|
|||||||
if (request.Ssl)
|
if (request.Ssl)
|
||||||
connectionString += ";SSL Mode=Require;";
|
connectionString += ";SSL Mode=Require;";
|
||||||
|
|
||||||
return SetDatabase<NpgsqlConnection, NpgsqlException>(connectionString, DbSettings.DatabaseEnum.PostgresSql);
|
|
||||||
|
SetDatabase<NpgsqlConnection, NpgsqlException>(connectionString, DatabaseType.PostgresSql);
|
||||||
|
cache.Set("database", new DatabaseResponse
|
||||||
|
{
|
||||||
|
Type = DatabaseType.PostgresSql,
|
||||||
|
Database = request.Database,
|
||||||
|
Password = request.Password,
|
||||||
|
Ssl = request.Ssl,
|
||||||
|
Port = request.Port,
|
||||||
|
Server = request.Server,
|
||||||
|
User = request.User
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("SetMysql")]
|
[HttpPost("SetMysql")]
|
||||||
@ -152,7 +175,19 @@ public class SetupController(
|
|||||||
if (request.Ssl)
|
if (request.Ssl)
|
||||||
connectionString += "SslMode=Require;";
|
connectionString += "SslMode=Require;";
|
||||||
|
|
||||||
return SetDatabase<MySqlConnection, MySqlException>(connectionString, DbSettings.DatabaseEnum.Mysql);
|
SetDatabase<MySqlConnection, MySqlException>(connectionString, DatabaseType.Mysql);
|
||||||
|
cache.Set("database", new DatabaseResponse
|
||||||
|
{
|
||||||
|
Type = DatabaseType.Mysql,
|
||||||
|
Database = request.Database,
|
||||||
|
Password = request.Password,
|
||||||
|
Ssl = request.Ssl,
|
||||||
|
Port = request.Port,
|
||||||
|
Server = request.Server,
|
||||||
|
User = request.User
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("SetSqlite")]
|
[HttpPost("SetSqlite")]
|
||||||
@ -179,14 +214,26 @@ public class SetupController(
|
|||||||
var filePath = Path.Combine(path, "database.db3");
|
var filePath = Path.Combine(path, "database.db3");
|
||||||
var connectionString = $"Data Source={filePath}";
|
var connectionString = $"Data Source={filePath}";
|
||||||
|
|
||||||
var result = SetDatabase<SqliteConnection, SqliteException>(connectionString, DbSettings.DatabaseEnum.Sqlite);
|
SetDatabase<SqliteConnection, SqliteException>(connectionString, DatabaseType.Sqlite);
|
||||||
|
|
||||||
foreach (var file in Directory.GetFiles(path))
|
foreach (var file in Directory.GetFiles(path))
|
||||||
System.IO.File.Delete(file);
|
System.IO.File.Delete(file);
|
||||||
|
|
||||||
return result;
|
cache.Set("database", new DatabaseResponse
|
||||||
|
{
|
||||||
|
Type = DatabaseType.Sqlite,
|
||||||
|
PathToDatabase = path
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("DatabaseConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<DatabaseRequest> DatabaseConfiguration() =>
|
||||||
|
cache.TryGetValue<DatabaseResponse>("database", out var response) ? Ok(response) : NoContent();
|
||||||
|
|
||||||
[HttpPost("SetRedis")]
|
[HttpPost("SetRedis")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
[BadRequestResponse]
|
[BadRequestResponse]
|
||||||
@ -205,10 +252,17 @@ public class SetupController(
|
|||||||
general.CacheSettings = new CacheSettings
|
general.CacheSettings = new CacheSettings
|
||||||
{
|
{
|
||||||
ConnectionString = connectionString,
|
ConnectionString = connectionString,
|
||||||
TypeDatabase = CacheSettings.CacheEnum.Redis
|
TypeDatabase = CacheType.Redis
|
||||||
};
|
};
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
|
cache.Set("cache", new CacheResponse
|
||||||
|
{
|
||||||
|
Type = CacheType.Redis,
|
||||||
|
Server = request.Server,
|
||||||
|
Password = request.Password,
|
||||||
|
Port = request.Port
|
||||||
|
});
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -226,20 +280,29 @@ public class SetupController(
|
|||||||
general.CacheSettings = new CacheSettings
|
general.CacheSettings = new CacheSettings
|
||||||
{
|
{
|
||||||
ConnectionString = null,
|
ConnectionString = null,
|
||||||
TypeDatabase = CacheSettings.CacheEnum.Memcached
|
TypeDatabase = CacheType.Memcached
|
||||||
};
|
};
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
|
cache.Set("cache", new CacheResponse
|
||||||
|
{
|
||||||
|
Type = CacheType.Memcached
|
||||||
|
});
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("CacheConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<CacheResponse> CacheConfiguration() =>
|
||||||
|
cache.TryGetValue<CacheResponse>("cache", out var response) ? Ok(response) : NoContent();
|
||||||
|
|
||||||
[HttpPost("CreateAdmin")]
|
[HttpPost("CreateAdmin")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
[BadRequestResponse]
|
[BadRequestResponse]
|
||||||
public ActionResult<string> CreateAdmin([FromBody] CreateUserRequest user)
|
public ActionResult<string> CreateAdmin([FromBody] CreateUserRequest user)
|
||||||
{
|
{
|
||||||
if (!PasswordHashService.HasPasswordInPolicySecurity(user.Password))
|
new PasswordPolicyService(GeneralConfig.PasswordPolicy).ValidatePasswordOrThrow(user.Password);
|
||||||
throw new ControllerArgumentException("The password must be at least 8 characters long and contain at least one uppercase letter and one special character.");
|
|
||||||
|
|
||||||
if (!MailAddress.TryCreate(user.Email, out _))
|
if (!MailAddress.TryCreate(user.Email, out _))
|
||||||
throw new ControllerArgumentException("The email address is incorrect.");
|
throw new ControllerArgumentException("The email address is incorrect.");
|
||||||
@ -258,6 +321,73 @@ public class SetupController(
|
|||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("UpdateAdminConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
public ActionResult UpdateAdminConfiguration()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(user.Value.Email))
|
||||||
|
return Ok();
|
||||||
|
|
||||||
|
if (!cache.TryGetValue<Admin>(CacheAdminKey, out var admin))
|
||||||
|
{
|
||||||
|
admin = user.Value;
|
||||||
|
cache.Set(CacheAdminKey, admin);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
admin!.OAuthProviders = user.Value.OAuthProviders;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(admin.Email))
|
||||||
|
admin.Email = user.Value.Email;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(admin.Username))
|
||||||
|
admin.Username = user.Value.Username;
|
||||||
|
|
||||||
|
cache.Set(CacheAdminKey, admin);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("AdminConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<UserResponse> AdminConfiguration() =>
|
||||||
|
cache.TryGetValue<Admin>(CacheAdminKey, out var admin) ? Ok(new UserResponse()
|
||||||
|
{
|
||||||
|
Email = admin!.Email,
|
||||||
|
Username = admin.Username,
|
||||||
|
TwoFactorAuthenticatorEnabled = admin.TwoFactorAuthenticator != TwoFactorAuthenticator.None,
|
||||||
|
UsedOAuthProviders = admin.OAuthProviders == null ? [] : admin.OAuthProviders.Keys.Select(x => x.ConvertToDto())
|
||||||
|
}) : NoContent();
|
||||||
|
|
||||||
|
[HttpGet("GenerateTotpKey")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
public ActionResult<string> GenerateTotpKey()
|
||||||
|
{
|
||||||
|
if (cache.TryGetValue<string>("totpSecret", out var secret))
|
||||||
|
return secret!;
|
||||||
|
|
||||||
|
secret = GeneratorKey.GenerateAlphaNumericBase32Compatible(16);
|
||||||
|
cache.Set("totpSecret", secret);
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("VerifyTotp")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
public ActionResult<bool> VerifyTotp([FromQuery] string code)
|
||||||
|
{
|
||||||
|
var isCorrect = cache.TryGetValue<string>("totpSecret", out var secret) &&
|
||||||
|
new TotpService(secret!).VerifyToken(code);
|
||||||
|
|
||||||
|
if (!isCorrect || !cache.TryGetValue<Admin>(CacheAdminKey, out var admin))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
admin!.Secret = secret;
|
||||||
|
admin.TwoFactorAuthenticator = TwoFactorAuthenticator.Totp;
|
||||||
|
cache.Set(CacheAdminKey, admin);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("SetLogging")]
|
[HttpPost("SetLogging")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
[BadRequestResponse]
|
[BadRequestResponse]
|
||||||
@ -292,9 +422,22 @@ public class SetupController(
|
|||||||
general.LogSettings = settings;
|
general.LogSettings = settings;
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
|
cache.Set("logging", new LoggingRequest
|
||||||
|
{
|
||||||
|
EnableLogToFile = settings.EnableLogToFile,
|
||||||
|
LogFileName = settings.LogFileName,
|
||||||
|
LogFilePath = settings.LogFilePath
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("LoggingConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<LoggingRequest> LoggingConfiguration() =>
|
||||||
|
cache.TryGetValue<LoggingRequest>("logging", out var data) ? Ok(data) : NoContent();
|
||||||
|
|
||||||
[HttpPost("SetEmail")]
|
[HttpPost("SetEmail")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
[BadRequestResponse]
|
[BadRequestResponse]
|
||||||
@ -318,9 +461,16 @@ public class SetupController(
|
|||||||
general.EmailSettings = settings;
|
general.EmailSettings = settings;
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
|
cache.Set("email", settings);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("EmailConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<EmailRequest> EmailConfiguration() =>
|
||||||
|
cache.TryGetValue<EmailRequest>("email", out var data) ? Ok(data) : NoContent();
|
||||||
|
|
||||||
[HttpPost("SetSchedule")]
|
[HttpPost("SetSchedule")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
[BadRequestResponse]
|
[BadRequestResponse]
|
||||||
@ -349,8 +499,20 @@ public class SetupController(
|
|||||||
|
|
||||||
GeneralConfig = general;
|
GeneralConfig = general;
|
||||||
|
|
||||||
|
cache.Set("schedule", new ScheduleConfigurationRequest()
|
||||||
|
{
|
||||||
|
StartTerm = general.ScheduleSettings.StartTerm,
|
||||||
|
CronUpdateSchedule = general.ScheduleSettings.CronUpdateSchedule
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("ScheduleConfiguration")]
|
||||||
|
[TokenAuthentication]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public ActionResult<ScheduleConfigurationRequest> ScheduleConfiguration() =>
|
||||||
|
cache.TryGetValue<ScheduleConfigurationRequest>("schedule", out var data) ? Ok(data) : NoContent();
|
||||||
|
|
||||||
[HttpPost("SetPasswordPolicy")]
|
[HttpPost("SetPasswordPolicy")]
|
||||||
[TokenAuthentication]
|
[TokenAuthentication]
|
||||||
public ActionResult<bool> SetPasswordPolicy([FromBody] PasswordPolicy? policy = null)
|
public ActionResult<bool> SetPasswordPolicy([FromBody] PasswordPolicy? policy = null)
|
||||||
|
Reference in New Issue
Block a user