Сборка бэк
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
namespace Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
|
||||
public interface ISettingsService
|
||||
{
|
||||
Task<SystemSettingsDto> GetSettingsAsync(CancellationToken cancellationToken = default);
|
||||
Task UpdateSettingsAsync(SystemSettingsDto settings, CancellationToken cancellationToken = default);
|
||||
SystemSettingsDto Current { get; }
|
||||
}
|
||||
|
||||
public interface ISystemSettings
|
||||
{
|
||||
SystemConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IStoriesSettings
|
||||
{
|
||||
StoriesConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IChatsSettings
|
||||
{
|
||||
ChatsConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IMessagesSettings
|
||||
{
|
||||
MessagesConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IWebRtcSettings
|
||||
{
|
||||
WebRtcConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IKlipySettings
|
||||
{
|
||||
KlipyConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IImportSettings
|
||||
{
|
||||
ImportConfig Current { get; }
|
||||
}
|
||||
|
||||
public interface IFederationSettings
|
||||
{
|
||||
FederationConfig Current { get; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using System.Security.Cryptography;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
|
||||
namespace Knot.Modules.Settings.Application.Settings.Commands;
|
||||
|
||||
public record UpdateSettingsCommand(SystemSettingsDto Settings) : ICommand<SystemSettingsDto>;
|
||||
|
||||
internal sealed class UpdateSettingsCommandHandler : ICommandHandler<UpdateSettingsCommand, SystemSettingsDto>
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public UpdateSettingsCommandHandler(ISettingsService settingsService, IMediator mediator)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async Task<Result<SystemSettingsDto>> Handle(UpdateSettingsCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Settings.Federation.Enabled &&
|
||||
(string.IsNullOrEmpty(request.Settings.Federation.PrivateKey) ||
|
||||
string.IsNullOrEmpty(request.Settings.Federation.PublicKey)))
|
||||
{
|
||||
using var rsa = RSA.Create(2048);
|
||||
request.Settings.Federation.PrivateKey = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey());
|
||||
request.Settings.Federation.PublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey());
|
||||
}
|
||||
|
||||
await _settingsService.UpdateSettingsAsync(request.Settings, cancellationToken);
|
||||
|
||||
// Notify other modules via Domain Event if needed, but here we can just publish
|
||||
await _mediator.Publish(new Knot.Modules.Settings.Domain.Events.SystemSettingsUpdatedDomainEvent(request.Settings), cancellationToken);
|
||||
|
||||
return Result.Success(request.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Knot.Shared.Kernel.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
namespace Knot.Modules.Settings.Application.Config.DTOs;
|
||||
namespace Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
public record PublicConfigDto
|
||||
{
|
||||
@@ -74,7 +74,7 @@ public record PublicConfigDto
|
||||
},
|
||||
Import = new ImportConfigDto
|
||||
{
|
||||
EnableTelegramImport = settings.Import.EnableTelegramImport
|
||||
Enabled = settings.Import.EnableTelegramImport
|
||||
},
|
||||
Federation = new FederationConfigDto
|
||||
{
|
||||
@@ -148,7 +148,7 @@ public record KlipyConfigDto
|
||||
|
||||
public record ImportConfigDto
|
||||
{
|
||||
public bool EnableTelegramImport { get; init; }
|
||||
public bool Enabled { get; init; }
|
||||
}
|
||||
|
||||
public record FederationConfigDto
|
||||
@@ -156,4 +156,4 @@ public record FederationConfigDto
|
||||
public bool Enabled { get; init; }
|
||||
public string ServerDescription { get; init; } = string.Empty;
|
||||
public List<FederationDomainConfig> AllowedDomains { get; init; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
public class SystemConfig
|
||||
{
|
||||
public string ServerTimezone { get; set; } = "UTC";
|
||||
public string DomainUrl { get; set; } = "https://example.com";
|
||||
public string AdminRoute { get; set; } = "admin";
|
||||
public bool EnableRegistration { get; set; } = true;
|
||||
}
|
||||
|
||||
public class StoriesConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public int MaxStoriesPerPeriod { get; set; } = 5;
|
||||
public int StoryLifetimeHours { get; set; } = 24;
|
||||
public bool TextStoriesEnabled { get; set; } = true;
|
||||
public int TextStoryDurationSeconds { get; set; } = 15;
|
||||
public int MediaStoryMaxDurationSeconds { get; set; } = 30;
|
||||
public int MaxMediaSizeBytes { get; set; } = 15 * 1024 * 1024;
|
||||
}
|
||||
|
||||
public class ChatsConfig
|
||||
{
|
||||
public bool SupportGroups { get; set; } = true;
|
||||
public int MaxGroupParticipants { get; set; } = 200000;
|
||||
public bool AutoCleanChats { get; set; } = false;
|
||||
public bool AllowChatToGroupConversion { get; set; } = true;
|
||||
public bool EnableFolders { get; set; } = true;
|
||||
}
|
||||
|
||||
public class MessagesConfig
|
||||
{
|
||||
public int DailyMessageLimitPerUser { get; set; } = 0;
|
||||
public int ChatMessageLimit { get; set; } = 0;
|
||||
public bool AllowMedia { get; set; } = true;
|
||||
public int MaxMediaSizeBytes { get; set; } = 50 * 1024 * 1024;
|
||||
public List<string> AllowedMediaTypes { get; set; } = new() { "image/jpeg", "image/png", "video/mp4", "image/gif" };
|
||||
public bool AllowVoiceMessages { get; set; } = true;
|
||||
public bool AllowForwarding { get; set; } = true;
|
||||
public bool AllowReactions { get; set; } = true;
|
||||
public bool AllowReplies { get; set; } = true;
|
||||
public bool AllowQuoting { get; set; } = true;
|
||||
public bool AllowMessageDeletion { get; set; } = true;
|
||||
public bool ForbidCopying { get; set; } = false;
|
||||
public bool AllowLinks { get; set; } = true;
|
||||
public bool AllowPolls { get; set; } = true;
|
||||
public bool AllowPinning { get; set; } = true;
|
||||
}
|
||||
|
||||
public class WebRtcConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public bool EnableVoiceCalls { get; set; } = true;
|
||||
public bool EnableVideoCalls { get; set; } = true;
|
||||
public bool EnableScreenSharing { get; set; } = true;
|
||||
public string TurnHost { get; set; } = string.Empty;
|
||||
public int TurnPort { get; set; } = 3478;
|
||||
public string TurnUser { get; set; } = string.Empty;
|
||||
public string TurnSecret { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class KlipyConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string AppName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ImportConfig
|
||||
{
|
||||
public bool EnableTelegramImport { get; set; } = false;
|
||||
}
|
||||
|
||||
public class FederationDomainConfig
|
||||
{
|
||||
public string Domain { get; set; } = string.Empty;
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
public string? PublicKey { get; set; }
|
||||
public RemoteCapabilities? Capabilities { get; set; }
|
||||
}
|
||||
|
||||
public class RemoteCapabilities
|
||||
{
|
||||
public bool AllowMedia { get; set; }
|
||||
public bool AllowPolls { get; set; }
|
||||
public bool AllowVoiceMessages { get; set; }
|
||||
public bool AllowVideoCalls { get; set; }
|
||||
public bool AllowScreenSharing { get; set; }
|
||||
}
|
||||
|
||||
public class FederationConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public string ServerDescription { get; set; } = string.Empty;
|
||||
public string? PrivateKey { get; set; }
|
||||
public string? PublicKey { get; set; }
|
||||
public List<FederationDomainConfig> AllowedDomains { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SystemSettingsDto
|
||||
{
|
||||
public SystemConfig System { get; set; } = new();
|
||||
public StoriesConfig Stories { get; set; } = new();
|
||||
public ChatsConfig Chats { get; set; } = new();
|
||||
public MessagesConfig Messages { get; set; } = new();
|
||||
public WebRtcConfig WebRtc { get; set; } = new();
|
||||
public KlipyConfig Klipy { get; set; } = new();
|
||||
public ImportConfig Import { get; set; } = new();
|
||||
public FederationConfig Federation { get; set; } = new();
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
using Knot.Modules.Settings.Application.Config.DTOs;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Shared.Kernel.Configuration;
|
||||
using MediatR;
|
||||
using Knot.Modules.Auth.Application.Auth.DTOs;
|
||||
|
||||
using Knot.Modules.Auth.Application.Users;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
namespace Host.Application.Config.Queries;
|
||||
namespace Knot.Modules.Settings.Application.Settings.Queries;
|
||||
|
||||
public record GetPublicConfigQuery : IQuery<PublicConfigDto>;
|
||||
public record GetPublicConfigQuery() : IQuery<PublicConfigDto>;
|
||||
|
||||
internal sealed class GetPublicConfigQueryHandler : IQueryHandler<GetPublicConfigQuery, PublicConfigDto>
|
||||
{
|
||||
@@ -25,4 +22,4 @@ internal sealed class GetPublicConfigQueryHandler : IQueryHandler<GetPublicConfi
|
||||
{
|
||||
return Task.FromResult(Result.Success(PublicConfigDto.FromSettings(_settings.Current)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
namespace Knot.Modules.Settings.Application.Settings.Queries;
|
||||
|
||||
public record GetSettingsQuery() : IQuery<SystemSettingsDto>;
|
||||
|
||||
internal sealed class GetSettingsQueryHandler : IQueryHandler<GetSettingsQuery, SystemSettingsDto>
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public GetSettingsQueryHandler(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
public async Task<Result<SystemSettingsDto>> Handle(GetSettingsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var settings = await _settingsService.GetSettingsAsync(cancellationToken);
|
||||
return Result.Success(settings);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,26 @@
|
||||
namespace Knot.Modules.Settings;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
public static class DependencyInjection {
|
||||
public static IServiceCollection AddSettingsModule(this IServiceCollection services) {
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
using Knot.Modules.Settings.Infrastructure.Configuration;
|
||||
|
||||
namespace Knot.Modules.Settings;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddSettingsModule(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSingleton<SettingsService>();
|
||||
|
||||
services.AddSingleton<ISettingsService>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<ISystemSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IStoriesSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IChatsSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IMessagesSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IWebRtcSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IKlipySettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IImportSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
services.AddSingleton<IFederationSettings>(sp => sp.GetRequiredService<SettingsService>());
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
namespace Knot.Modules.Settings.Domain.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Domain Event: System settings updated.
|
||||
/// Owned by Settings module.
|
||||
/// </summary>
|
||||
public sealed record SystemSettingsUpdatedDomainEvent(SystemSettingsDto Settings) : IDomainEvent;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Knot.Shared.Infrastructure.Persistence;
|
||||
using Knot.Shared.Infrastructure.Persistence.Entities;
|
||||
using Knot.Shared.Kernel.Security;
|
||||
using Knot.Modules.Settings.Application.Settings.Abstractions;
|
||||
using Knot.Modules.Settings.Application.Settings.DTOs;
|
||||
|
||||
namespace Knot.Modules.Settings.Infrastructure.Configuration;
|
||||
|
||||
public class SettingsService : ISettingsService,
|
||||
ISystemSettings,
|
||||
IStoriesSettings,
|
||||
IChatsSettings,
|
||||
IMessagesSettings,
|
||||
IWebRtcSettings,
|
||||
IKlipySettings,
|
||||
IImportSettings,
|
||||
IFederationSettings
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IEncryptionService _encryptionService;
|
||||
private SystemSettingsDto _current;
|
||||
|
||||
public SettingsService(IServiceProvider serviceProvider, IEncryptionService encryptionService)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_encryptionService = encryptionService;
|
||||
_current = new SystemSettingsDto();
|
||||
}
|
||||
|
||||
public SystemSettingsDto Current => _current;
|
||||
|
||||
SystemConfig ISystemSettings.Current => _current.System;
|
||||
StoriesConfig IStoriesSettings.Current => _current.Stories;
|
||||
ChatsConfig IChatsSettings.Current => _current.Chats;
|
||||
MessagesConfig IMessagesSettings.Current => _current.Messages;
|
||||
WebRtcConfig IWebRtcSettings.Current => _current.WebRtc;
|
||||
KlipyConfig IKlipySettings.Current => _current.Klipy;
|
||||
ImportConfig IImportSettings.Current => _current.Import;
|
||||
FederationConfig IFederationSettings.Current => _current.Federation;
|
||||
|
||||
public async Task<SystemSettingsDto> GetSettingsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SystemDbContext>();
|
||||
|
||||
var setting = await db.Settings.FirstOrDefaultAsync(s => s.Key == "Global", cancellationToken);
|
||||
if (setting == null)
|
||||
{
|
||||
return new SystemSettingsDto();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = _encryptionService.DecryptMessage(setting.EncryptedValue);
|
||||
var dto = JsonSerializer.Deserialize<SystemSettingsDto>(json);
|
||||
if (dto != null)
|
||||
{
|
||||
_current = dto;
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fail safe
|
||||
}
|
||||
|
||||
return new SystemSettingsDto();
|
||||
}
|
||||
|
||||
public async Task UpdateSettingsAsync(SystemSettingsDto settings, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SystemDbContext>();
|
||||
|
||||
var json = JsonSerializer.Serialize(settings);
|
||||
var encrypted = _encryptionService.EncryptMessage(json);
|
||||
|
||||
var setting = await db.Settings.FirstOrDefaultAsync(s => s.Key == "Global", cancellationToken);
|
||||
if (setting == null)
|
||||
{
|
||||
db.Settings.Add(new SystemSetting { Key = "Global", EncryptedValue = encrypted });
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.EncryptedValue = encrypted;
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
_current = settings;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_current = GetSettingsAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,12 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" />
|
||||
<ProjectReference Include="..\Auth\Knot.Modules.Auth.csproj" />
|
||||
<ProjectReference Include="..\Stories\Knot.Modules.Stories.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Carter" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using Carter;
|
||||
using MediatR;
|
||||
using Knot.Shared.Kernel.Constants;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Host.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// РегРСвЂВВстрацРСвЂВВР РЋР РЏ РЎРЊР Р…Р ТвЂВВРїРѕРСвЂВВнтовРТвЂВВля РєРѕРЅС„РСвЂВВгурацРСвЂВВРцРїСЂРСвЂВВложенРСвЂВВР РЋР РЏ.
|
||||
/// </summary>
|
||||
public sealed class SettingsEndpoints : ICarterModule
|
||||
{
|
||||
public void AddRoutes(IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup(Routes.ApiConfig);
|
||||
|
||||
group.MapGet("/", async (ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new Host.Application.Config.Queries.GetPublicConfigQuery(), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user