Add settings endpoints and registration status check: implement new API endpoints for settings management, including registration status retrieval, and update the registration command handler to enforce registration rules based on site settings.

This commit is contained in:
Leonid Pershin
2026-07-25 07:48:17 +03:00
parent 1bdc3323ab
commit c53848477f
33 changed files with 1289 additions and 17 deletions
@@ -26,4 +26,9 @@ public static class AuthErrors
"Auth.UserNameTaken",
"Это имя пользователя уже занято."
);
public static readonly Error RegistrationDisabled = Error.Forbidden(
"Auth.RegistrationDisabled",
"Регистрация на сайте отключена администратором."
);
}
@@ -7,7 +7,8 @@ namespace TeleWave.Application.Auth.Register;
public sealed class RegisterCommandHandler(
IIdentityService identityService,
IJwtTokenService jwtTokenService,
IRefreshTokenService refreshTokenService
IRefreshTokenService refreshTokenService,
ISiteSettings siteSettings
) : ICommandHandler<RegisterCommand, Result<AuthResult>>
{
public async Task<Result<AuthResult>> Handle(
@@ -15,6 +16,10 @@ public sealed class RegisterCommandHandler(
CancellationToken cancellationToken
)
{
// Открытая регистрация должна быть явно включена админом; иначе учётки заводит только он.
if (!await siteSettings.IsRegistrationEnabledAsync(cancellationToken))
return Result.Failure<AuthResult>(AuthErrors.RegistrationDisabled);
var createResult = await identityService.CreateUserAsync(
command.UserName,
command.Password,
@@ -3,6 +3,7 @@ using TeleWave.Domain.Auth;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using TeleWave.Domain.Settings;
namespace TeleWave.Application.Common.Interfaces;
@@ -14,6 +15,7 @@ public interface IAppDbContext
DbSet<Channel> Channels { get; }
DbSet<ScheduleEntry> ScheduleEntries { get; }
DbSet<BumperAsset> BumperAssets { get; }
DbSet<AppSetting> AppSettings { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
@@ -0,0 +1,10 @@
namespace TeleWave.Application.Common.Interfaces;
/// <summary>Доступ к глобальным настройкам сайта (key-value), скрывающий хранилище от хендлеров.</summary>
public interface ISiteSettings
{
Task<bool> IsRegistrationEnabledAsync(CancellationToken cancellationToken);
/// <summary>Пишет значение в контекст (сохранение — за UnitOfWorkBehavior вызывающей команды).</summary>
Task SetRegistrationEnabledAsync(bool enabled, CancellationToken cancellationToken);
}
@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
namespace TeleWave.Application.Settings;
/// <summary>Чтение типизированных значений из key-value настроек с дефолтом при отсутствии ключа.</summary>
public static class AppSettingsReader
{
public static async Task<bool> GetBoolSettingAsync(
this IAppDbContext db,
string key,
bool defaultValue,
CancellationToken cancellationToken
)
{
var value = await db.AppSettings.AsNoTracking()
.Where(s => s.Key == key)
.Select(s => s.Value)
.FirstOrDefaultAsync(cancellationToken);
return value is not null && bool.TryParse(value, out var parsed) ? parsed : defaultValue;
}
}
@@ -0,0 +1,5 @@
using LiteCqrs;
namespace TeleWave.Application.Settings.GetSiteSettings;
public sealed record GetSiteSettingsQuery : IQuery<SiteSettingsDto>;
@@ -0,0 +1,17 @@
using LiteCqrs;
using TeleWave.Application.Common.Interfaces;
namespace TeleWave.Application.Settings.GetSiteSettings;
public sealed class GetSiteSettingsQueryHandler(ISiteSettings siteSettings)
: IQueryHandler<GetSiteSettingsQuery, SiteSettingsDto>
{
public async Task<SiteSettingsDto> Handle(
GetSiteSettingsQuery query,
CancellationToken cancellationToken
)
{
var registrationEnabled = await siteSettings.IsRegistrationEnabledAsync(cancellationToken);
return new SiteSettingsDto(registrationEnabled);
}
}
@@ -0,0 +1,8 @@
namespace TeleWave.Application.Settings;
/// <summary>Стабильные ключи глобальных настроек сайта в key-value хранилище (AppSetting).</summary>
public static class SettingKeys
{
/// <summary>Разрешена ли открытая регистрация пользователей (по умолчанию — нет).</summary>
public const string RegistrationEnabled = "registration.enabled";
}
@@ -0,0 +1,4 @@
namespace TeleWave.Application.Settings;
/// <summary>Глобальные настройки сайта, управляемые администратором.</summary>
public sealed record SiteSettingsDto(bool RegistrationEnabled);
@@ -0,0 +1,6 @@
using LiteCqrs;
using TeleWave.Application.Common.Models;
namespace TeleWave.Application.Settings.UpdateSiteSettings;
public sealed record UpdateSiteSettingsCommand(bool RegistrationEnabled) : ICommand<Result>;
@@ -0,0 +1,19 @@
using LiteCqrs;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
namespace TeleWave.Application.Settings.UpdateSiteSettings;
public sealed class UpdateSiteSettingsCommandHandler(ISiteSettings siteSettings)
: ICommandHandler<UpdateSiteSettingsCommand, Result>
{
public async Task<Result> Handle(
UpdateSiteSettingsCommand command,
CancellationToken cancellationToken
)
{
// Сохранение выполняет UnitOfWorkBehavior команды.
await siteSettings.SetRegistrationEnabledAsync(command.RegistrationEnabled, cancellationToken);
return Result.Success();
}
}