2024-09-18 06:00:07 +03:00
|
|
|
|
using Asp.Versioning;
|
|
|
|
|
using Cronos;
|
2024-05-28 07:19:40 +03:00
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2024-05-29 03:42:39 +03:00
|
|
|
|
using Microsoft.Data.Sqlite;
|
2024-05-29 03:35:52 +03:00
|
|
|
|
using Microsoft.Extensions.Caching.Memory;
|
2024-12-18 07:39:17 +03:00
|
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
using Mirea.Api.Dto.Common;
|
2024-05-29 03:42:39 +03:00
|
|
|
|
using Mirea.Api.Dto.Requests;
|
|
|
|
|
using Mirea.Api.Dto.Requests.Configuration;
|
2024-12-18 07:39:17 +03:00
|
|
|
|
using Mirea.Api.Dto.Responses;
|
|
|
|
|
using Mirea.Api.Dto.Responses.Configuration;
|
2024-05-28 07:19:40 +03:00
|
|
|
|
using Mirea.Api.Endpoint.Common.Attributes;
|
|
|
|
|
using Mirea.Api.Endpoint.Common.Exceptions;
|
|
|
|
|
using Mirea.Api.Endpoint.Common.Interfaces;
|
2024-12-18 07:39:17 +03:00
|
|
|
|
using Mirea.Api.Endpoint.Common.MapperDto;
|
2024-05-29 03:35:52 +03:00
|
|
|
|
using Mirea.Api.Endpoint.Common.Services;
|
2024-10-07 02:13:35 +03:00
|
|
|
|
using Mirea.Api.Endpoint.Configuration.Model;
|
2024-10-07 02:25:36 +03:00
|
|
|
|
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
|
|
|
|
|
using Mirea.Api.Endpoint.Configuration.Validation.Validators;
|
2024-12-18 07:39:17 +03:00
|
|
|
|
using Mirea.Api.Security.Common.Domain;
|
2024-06-01 08:19:26 +03:00
|
|
|
|
using Mirea.Api.Security.Services;
|
2024-05-29 03:42:39 +03:00
|
|
|
|
using MySqlConnector;
|
|
|
|
|
using Npgsql;
|
2024-12-22 07:13:59 +03:00
|
|
|
|
using Serilog;
|
2024-05-29 03:43:08 +03:00
|
|
|
|
using StackExchange.Redis;
|
2024-05-29 03:37:04 +03:00
|
|
|
|
using System;
|
2024-06-10 22:15:15 +03:00
|
|
|
|
using System.Collections.Generic;
|
2024-05-29 03:37:04 +03:00
|
|
|
|
using System.Data;
|
2024-05-29 03:46:16 +03:00
|
|
|
|
using System.IO;
|
2024-09-07 04:19:05 +03:00
|
|
|
|
using System.Linq;
|
2024-05-29 03:46:16 +03:00
|
|
|
|
using System.Runtime.InteropServices;
|
2024-12-22 05:25:19 +03:00
|
|
|
|
using System.Security;
|
2024-05-29 03:46:16 +03:00
|
|
|
|
using System.Security.Cryptography;
|
2024-12-18 07:27:57 +03:00
|
|
|
|
using PasswordPolicy = Mirea.Api.Dto.Common.PasswordPolicy;
|
2024-05-28 07:19:40 +03:00
|
|
|
|
|
|
|
|
|
namespace Mirea.Api.Endpoint.Controllers.Configuration;
|
|
|
|
|
|
|
|
|
|
[ApiVersion("1.0")]
|
|
|
|
|
[MaintenanceModeIgnore]
|
2024-06-01 07:33:08 +03:00
|
|
|
|
[ApiExplorerSettings(IgnoreApi = true)]
|
2024-08-27 22:51:14 +03:00
|
|
|
|
public class SetupController(
|
2024-06-01 08:20:27 +03:00
|
|
|
|
ISetupToken setupToken,
|
|
|
|
|
IMaintenanceModeNotConfigureService notConfigureService,
|
|
|
|
|
IMemoryCache cache,
|
2024-12-18 07:39:17 +03:00
|
|
|
|
PasswordHashService passwordHashService,
|
|
|
|
|
IOptionsSnapshot<Admin> user) : BaseController
|
2024-05-28 07:19:40 +03:00
|
|
|
|
{
|
2024-06-01 06:27:49 +03:00
|
|
|
|
private const string CacheGeneralKey = "config_general";
|
|
|
|
|
private const string CacheAdminKey = "config_admin";
|
|
|
|
|
|
2024-05-29 03:35:52 +03:00
|
|
|
|
private GeneralConfig GeneralConfig
|
2024-06-01 06:29:16 +03:00
|
|
|
|
{
|
2024-05-29 03:35:52 +03:00
|
|
|
|
get => cache.Get<GeneralConfig>(CacheGeneralKey) ?? new GeneralConfig();
|
|
|
|
|
set => cache.Set(CacheGeneralKey, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 07:19:40 +03:00
|
|
|
|
[HttpGet("GenerateToken")]
|
2024-05-29 03:30:26 +03:00
|
|
|
|
[Localhost]
|
2024-05-28 07:19:40 +03:00
|
|
|
|
public ActionResult<string> GenerateToken()
|
|
|
|
|
{
|
|
|
|
|
if (!notConfigureService.IsMaintenanceMode)
|
|
|
|
|
throw new ControllerArgumentException(
|
|
|
|
|
"The token cannot be generated because the server has been configured. " +
|
2024-07-04 23:45:33 +03:00
|
|
|
|
$"If you need to restart the configuration, then delete the \"{GeneralConfig.FilePath}\" file and restart the application.");
|
2024-05-28 07:19:40 +03:00
|
|
|
|
|
|
|
|
|
var token = new byte[32];
|
|
|
|
|
RandomNumberGenerator.Create().GetBytes(token);
|
|
|
|
|
setupToken.SetToken(token);
|
|
|
|
|
|
|
|
|
|
return Ok(Convert.ToBase64String(token));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-08 01:56:14 +03:00
|
|
|
|
[HttpGet("IsConfigured")]
|
|
|
|
|
public ActionResult<bool> IsConfigured() =>
|
|
|
|
|
!notConfigureService.IsMaintenanceMode;
|
|
|
|
|
|
2024-05-28 07:19:40 +03:00
|
|
|
|
[HttpGet("CheckToken")]
|
|
|
|
|
public ActionResult<bool> CheckToken([FromQuery] string token)
|
|
|
|
|
{
|
2024-12-18 07:32:00 +03:00
|
|
|
|
byte[] tokenBase64;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
tokenBase64 = Convert.FromBase64String(token);
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException)
|
|
|
|
|
{
|
|
|
|
|
throw new ControllerArgumentException("A token of the wrong format.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!setupToken.MatchToken(tokenBase64))
|
2024-12-22 05:25:19 +03:00
|
|
|
|
throw new SecurityException("The token is not valid");
|
2024-05-28 07:19:40 +03:00
|
|
|
|
|
2024-09-07 04:19:51 +03:00
|
|
|
|
Response.Cookies.Append(TokenAuthenticationAttribute.AuthToken, token, new CookieOptions
|
2024-05-28 07:19:40 +03:00
|
|
|
|
{
|
2024-07-05 01:59:36 +03:00
|
|
|
|
Path = UrlHelper.GetSubPathWithoutFirstApiName + "api",
|
2024-08-10 23:11:43 +03:00
|
|
|
|
Domain = HttpContext.GetCurrentDomain(),
|
2024-08-24 02:25:29 +03:00
|
|
|
|
HttpOnly = true,
|
2024-08-10 23:11:43 +03:00
|
|
|
|
#if !DEBUG
|
2024-08-24 02:25:29 +03:00
|
|
|
|
Secure = true
|
2024-08-10 23:11:43 +03:00
|
|
|
|
#endif
|
2024-05-28 07:19:40 +03:00
|
|
|
|
});
|
|
|
|
|
return Ok(true);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[HttpGet("IsConfiguredToken")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
public ActionResult<bool> IsConfiguredToken() =>
|
|
|
|
|
Ok(true);
|
|
|
|
|
|
|
|
|
|
private void SetDatabase<TConnection, TException>(string connectionString, DatabaseType databaseType)
|
2024-05-29 03:37:04 +03:00
|
|
|
|
where TConnection : class, IDbConnection, new()
|
|
|
|
|
where TException : Exception
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2024-09-08 03:24:32 +03:00
|
|
|
|
using (var connection = new TConnection())
|
|
|
|
|
{
|
|
|
|
|
connection.ConnectionString = connectionString;
|
|
|
|
|
connection.Open();
|
|
|
|
|
connection.Close();
|
2024-05-29 03:37:04 +03:00
|
|
|
|
|
2024-09-07 04:19:05 +03:00
|
|
|
|
if (connection is SqliteConnection)
|
|
|
|
|
SqliteConnection.ClearAllPools();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 03:37:04 +03:00
|
|
|
|
var general = GeneralConfig;
|
|
|
|
|
general.DbSettings = new DbSettings
|
|
|
|
|
{
|
|
|
|
|
ConnectionStringSql = connectionString,
|
|
|
|
|
TypeDatabase = databaseType
|
|
|
|
|
};
|
|
|
|
|
GeneralConfig = general;
|
2024-06-01 06:29:16 +03:00
|
|
|
|
}
|
2024-05-29 03:37:04 +03:00
|
|
|
|
catch (TException ex)
|
|
|
|
|
{
|
|
|
|
|
throw new ControllerArgumentException($"Error when connecting: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 03:38:21 +03:00
|
|
|
|
[HttpPost("SetPsql")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> SetPsql([FromBody] DatabaseRequest request)
|
|
|
|
|
{
|
2024-12-25 05:43:30 +03:00
|
|
|
|
var connectionString = $"Host={request.Server}:{request.Port};Username={request.User};Database={request.Database}";
|
2024-05-29 03:38:21 +03:00
|
|
|
|
if (request.Password != null)
|
|
|
|
|
connectionString += $";Password={request.Password}";
|
|
|
|
|
if (request.Ssl)
|
|
|
|
|
connectionString += ";SSL Mode=Require;";
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
|
|
|
|
|
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);
|
2024-05-29 03:38:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("SetMysql")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> SetMysql([FromBody] DatabaseRequest request)
|
|
|
|
|
{
|
2024-12-25 05:43:30 +03:00
|
|
|
|
var connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database};";
|
2024-05-29 03:38:21 +03:00
|
|
|
|
if (request.Password != null)
|
2024-06-01 06:28:13 +03:00
|
|
|
|
connectionString += $"Pwd={request.Password};";
|
2024-05-29 03:38:21 +03:00
|
|
|
|
if (request.Ssl)
|
2024-06-01 06:28:13 +03:00
|
|
|
|
connectionString += "SslMode=Require;";
|
2024-05-29 03:38:21 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
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);
|
2024-05-29 03:38:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("SetSqlite")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
public ActionResult<bool> 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);
|
|
|
|
|
}
|
2024-09-07 04:19:05 +03:00
|
|
|
|
else if (Directory.GetDirectories(path).Length != 0 ||
|
|
|
|
|
!Directory.GetFiles(path).Select(x => string.Equals(Path.GetFileName(x), "database.db3")).All(x => x))
|
2024-10-27 06:51:05 +03:00
|
|
|
|
{
|
2024-05-29 03:38:21 +03:00
|
|
|
|
throw new ControllerArgumentException("Such a folder exists. Enter a different name");
|
2024-10-27 06:51:05 +03:00
|
|
|
|
}
|
2024-05-29 03:38:21 +03:00
|
|
|
|
|
2024-09-07 04:19:05 +03:00
|
|
|
|
var filePath = Path.Combine(path, "database.db3");
|
|
|
|
|
var connectionString = $"Data Source={filePath}";
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
SetDatabase<SqliteConnection, SqliteException>(connectionString, DatabaseType.Sqlite);
|
2024-09-07 04:19:05 +03:00
|
|
|
|
|
|
|
|
|
foreach (var file in Directory.GetFiles(path))
|
|
|
|
|
System.IO.File.Delete(file);
|
2024-05-29 03:38:21 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("database", new DatabaseResponse
|
|
|
|
|
{
|
|
|
|
|
Type = DatabaseType.Sqlite,
|
|
|
|
|
PathToDatabase = path
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Ok(true);
|
2024-05-29 03:38:21 +03:00
|
|
|
|
}
|
2024-05-29 03:43:08 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[HttpGet("DatabaseConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<DatabaseRequest> DatabaseConfiguration() =>
|
|
|
|
|
cache.TryGetValue<DatabaseResponse>("database", out var response) ? Ok(response) : NoContent();
|
|
|
|
|
|
2024-05-29 03:43:08 +03:00
|
|
|
|
[HttpPost("SetRedis")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> SetRedis([FromBody] CacheRequest request)
|
|
|
|
|
{
|
2024-12-25 05:43:30 +03:00
|
|
|
|
var connectionString = $"{request.Server}:{request.Port},ssl=false";
|
2024-05-29 03:43:08 +03:00
|
|
|
|
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,
|
2024-12-18 07:39:17 +03:00
|
|
|
|
TypeDatabase = CacheType.Redis
|
2024-05-29 03:43:08 +03:00
|
|
|
|
};
|
|
|
|
|
GeneralConfig = general;
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("cache", new CacheResponse
|
|
|
|
|
{
|
|
|
|
|
Type = CacheType.Redis,
|
|
|
|
|
Server = request.Server,
|
|
|
|
|
Password = request.Password,
|
|
|
|
|
Port = request.Port
|
|
|
|
|
});
|
2024-05-29 03:43:08 +03:00
|
|
|
|
return Ok(true);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
throw new ControllerArgumentException("Error when connecting to Redis: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("SetMemcached")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> SetMemcached()
|
|
|
|
|
{
|
|
|
|
|
var general = GeneralConfig;
|
|
|
|
|
general.CacheSettings = new CacheSettings
|
|
|
|
|
{
|
|
|
|
|
ConnectionString = null,
|
2024-12-18 07:39:17 +03:00
|
|
|
|
TypeDatabase = CacheType.Memcached
|
2024-05-29 03:43:08 +03:00
|
|
|
|
};
|
|
|
|
|
GeneralConfig = general;
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("cache", new CacheResponse
|
|
|
|
|
{
|
|
|
|
|
Type = CacheType.Memcached
|
|
|
|
|
});
|
2024-05-29 03:43:08 +03:00
|
|
|
|
return Ok(true);
|
|
|
|
|
}
|
2024-05-29 03:44:24 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[HttpGet("CacheConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<CacheResponse> CacheConfiguration() =>
|
|
|
|
|
cache.TryGetValue<CacheResponse>("cache", out var response) ? Ok(response) : NoContent();
|
|
|
|
|
|
2024-06-01 06:27:49 +03:00
|
|
|
|
[HttpPost("CreateAdmin")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
2024-12-23 06:28:28 +03:00
|
|
|
|
public ActionResult<string> CreateAdmin([FromBody] CreateUserRequest userRequest)
|
2024-06-01 06:27:49 +03:00
|
|
|
|
{
|
2024-12-23 06:28:28 +03:00
|
|
|
|
new PasswordPolicyService(GeneralConfig.PasswordPolicy).ValidatePasswordOrThrow(userRequest.Password);
|
|
|
|
|
var (salt, hash) = passwordHashService.HashPassword(userRequest.Password);
|
2024-06-01 08:20:27 +03:00
|
|
|
|
|
|
|
|
|
var admin = new Admin
|
|
|
|
|
{
|
2024-12-23 06:28:28 +03:00
|
|
|
|
Username = userRequest.Username,
|
|
|
|
|
Email = userRequest.Email,
|
2024-06-01 08:20:27 +03:00
|
|
|
|
PasswordHash = hash,
|
|
|
|
|
Salt = salt
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
cache.Set(CacheAdminKey, admin);
|
2024-06-01 06:27:49 +03:00
|
|
|
|
return Ok(true);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 03:44:24 +03:00
|
|
|
|
[HttpPost("SetLogging")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
2024-06-01 07:26:22 +03:00
|
|
|
|
public ActionResult<bool> SetLogging([FromBody] LoggingRequest? request = null)
|
2024-05-29 03:44:24 +03:00
|
|
|
|
{
|
|
|
|
|
var settings = (request == null) switch
|
|
|
|
|
{
|
|
|
|
|
true => new LogSettings
|
|
|
|
|
{
|
2024-06-10 22:04:29 +03:00
|
|
|
|
EnableLogToFile = true
|
2024-05-29 03:44:24 +03:00
|
|
|
|
},
|
|
|
|
|
false => new LogSettings
|
|
|
|
|
{
|
|
|
|
|
EnableLogToFile = request.EnableLogToFile,
|
|
|
|
|
LogFileName = request.LogFileName,
|
|
|
|
|
LogFilePath = request.LogFilePath
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-22 07:13:59 +03:00
|
|
|
|
if (!string.IsNullOrEmpty(request?.ApiServerSeq))
|
|
|
|
|
{
|
|
|
|
|
settings.ApiServerSeq = request.ApiServerSeq;
|
|
|
|
|
settings.ApiKeySeq = request.ApiKeySeq;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Log.Logger = new LoggerConfiguration()
|
|
|
|
|
.WriteTo.Seq(settings.ApiServerSeq, apiKey: settings.ApiKeySeq)
|
|
|
|
|
.CreateLogger();
|
|
|
|
|
|
|
|
|
|
Log.Warning("Testing configuration Seq.");
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// ignoring
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
Log.CloseAndFlush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-10 22:04:29 +03:00
|
|
|
|
if (settings.EnableLogToFile)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(settings.LogFileName))
|
|
|
|
|
settings.LogFileName = "log-";
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(settings.LogFilePath))
|
|
|
|
|
settings.LogFilePath = OperatingSystem.IsWindows() || PathBuilder.IsDefaultPath ?
|
|
|
|
|
PathBuilder.Combine("logs") :
|
|
|
|
|
"/var/log/mirea";
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 03:44:24 +03:00
|
|
|
|
var general = GeneralConfig;
|
|
|
|
|
general.LogSettings = settings;
|
|
|
|
|
GeneralConfig = general;
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("logging", new LoggingRequest
|
|
|
|
|
{
|
|
|
|
|
EnableLogToFile = settings.EnableLogToFile,
|
|
|
|
|
LogFileName = settings.LogFileName,
|
2024-12-22 07:13:59 +03:00
|
|
|
|
LogFilePath = settings.LogFilePath,
|
|
|
|
|
ApiKeySeq = settings.ApiKeySeq,
|
|
|
|
|
ApiServerSeq = settings.ApiServerSeq
|
2024-12-18 07:39:17 +03:00
|
|
|
|
});
|
|
|
|
|
|
2024-05-29 03:44:24 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-05-29 03:45:02 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[HttpGet("LoggingConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<LoggingRequest> LoggingConfiguration() =>
|
|
|
|
|
cache.TryGetValue<LoggingRequest>("logging", out var data) ? Ok(data) : NoContent();
|
|
|
|
|
|
2024-05-29 03:45:02 +03:00
|
|
|
|
[HttpPost("SetEmail")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
2024-06-01 07:26:22 +03:00
|
|
|
|
public ActionResult<bool> SetEmail([FromBody] EmailRequest? request = null)
|
2024-05-29 03:45:02 +03:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("email", settings);
|
2024-05-29 03:45:02 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-05-29 03:46:16 +03:00
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
[HttpGet("EmailConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<EmailRequest> EmailConfiguration() =>
|
|
|
|
|
cache.TryGetValue<EmailRequest>("email", out var data) ? Ok(data) : NoContent();
|
|
|
|
|
|
2024-05-29 03:46:16 +03:00
|
|
|
|
[HttpPost("SetSchedule")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> SetSchedule([FromBody] ScheduleConfigurationRequest request)
|
|
|
|
|
{
|
|
|
|
|
var general = GeneralConfig;
|
|
|
|
|
general.ScheduleSettings = new ScheduleSettings
|
|
|
|
|
{
|
|
|
|
|
// every 6 hours
|
|
|
|
|
CronUpdateSchedule = request.CronUpdateSchedule ?? "0 */6 * * *",
|
|
|
|
|
StartTerm = request.StartTerm,
|
2024-06-10 22:03:48 +03:00
|
|
|
|
PairPeriod = new Dictionary<int, ScheduleSettings.PairPeriodTime>
|
|
|
|
|
{
|
|
|
|
|
{1, new ScheduleSettings.PairPeriodTime(new TimeOnly(9, 0, 0), new TimeOnly(10, 30, 0))},
|
|
|
|
|
{2, new ScheduleSettings.PairPeriodTime(new TimeOnly(10, 40, 0), new TimeOnly(12, 10, 0))},
|
|
|
|
|
{3, new ScheduleSettings.PairPeriodTime(new TimeOnly(12, 40, 0), new TimeOnly(14, 10, 0))},
|
|
|
|
|
{4, new ScheduleSettings.PairPeriodTime(new TimeOnly(14, 20, 0), new TimeOnly(15, 50, 0))},
|
|
|
|
|
{5, new ScheduleSettings.PairPeriodTime(new TimeOnly(16, 20, 0), new TimeOnly(17, 50, 0))},
|
|
|
|
|
{6, new ScheduleSettings.PairPeriodTime(new TimeOnly(18, 0, 0), new TimeOnly(19, 30, 0))},
|
|
|
|
|
{7, new ScheduleSettings.PairPeriodTime(new TimeOnly(19, 40, 0), new TimeOnly(21, 10, 0))},
|
|
|
|
|
}
|
2024-05-29 03:46:16 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2024-12-18 07:39:17 +03:00
|
|
|
|
cache.Set("schedule", new ScheduleConfigurationRequest()
|
|
|
|
|
{
|
|
|
|
|
StartTerm = general.ScheduleSettings.StartTerm,
|
|
|
|
|
CronUpdateSchedule = general.ScheduleSettings.CronUpdateSchedule
|
|
|
|
|
});
|
2024-05-29 03:46:16 +03:00
|
|
|
|
return true;
|
2024-05-29 03:38:21 +03:00
|
|
|
|
}
|
2024-12-18 07:39:17 +03:00
|
|
|
|
|
|
|
|
|
[HttpGet("ScheduleConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<ScheduleConfigurationRequest> ScheduleConfiguration() =>
|
|
|
|
|
cache.TryGetValue<ScheduleConfigurationRequest>("schedule", out var data) ? Ok(data) : NoContent();
|
|
|
|
|
|
2024-12-18 07:27:57 +03:00
|
|
|
|
[HttpPost("SetPasswordPolicy")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
public ActionResult<bool> SetPasswordPolicy([FromBody] PasswordPolicy? policy = null)
|
|
|
|
|
{
|
|
|
|
|
GeneralConfig.PasswordPolicy = policy?.ConvertFromDto() ?? new Security.Common.Domain.PasswordPolicy();
|
|
|
|
|
cache.Set("password", true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("PasswordPolicyConfiguration")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
|
|
|
public ActionResult<PasswordPolicy> PasswordPolicyConfiguration() =>
|
|
|
|
|
cache.TryGetValue("password", out _) ? Ok(GeneralConfig.PasswordPolicy) : NoContent();
|
2024-05-29 03:38:21 +03:00
|
|
|
|
|
2024-06-01 06:29:16 +03:00
|
|
|
|
[HttpPost("Submit")]
|
|
|
|
|
[TokenAuthentication]
|
|
|
|
|
[BadRequestResponse]
|
|
|
|
|
public ActionResult<bool> Submit()
|
|
|
|
|
{
|
|
|
|
|
if (!new SettingsRequiredValidator(GeneralConfig).AreSettingsValid())
|
|
|
|
|
throw new ControllerArgumentException("The necessary data has not been configured.");
|
2024-05-28 07:19:40 +03:00
|
|
|
|
|
2024-06-01 08:20:27 +03:00
|
|
|
|
if (!cache.TryGetValue(CacheAdminKey, out Admin? admin) || admin == null)
|
2024-06-01 06:29:16 +03:00
|
|
|
|
throw new ControllerArgumentException("The administrator's data was not set.");
|
|
|
|
|
|
2024-07-04 23:54:17 +03:00
|
|
|
|
admin.SaveSetting();
|
2024-07-04 23:45:33 +03:00
|
|
|
|
GeneralConfig.SaveSetting();
|
2024-06-01 06:29:16 +03:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-05-28 07:19:40 +03:00
|
|
|
|
}
|