SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/ Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue, контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер), Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог). Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0, тесты 1340/130/52/38/9 зелёные.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
using System.Text.Json;
|
||||
using Deal.Api.Extensions;
|
||||
using Deal.Api.Services;
|
||||
using Deal.Modules.Settings.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Models;
|
||||
using Deal.Modules.Settings.Application.Services;
|
||||
using Deal.Modules.Tenants.Application.Models;
|
||||
|
||||
namespace Deal.Api.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP-эндпоинты настроек тенанта
|
||||
/// </summary>
|
||||
public static class SettingsEndpoints
|
||||
{
|
||||
private const string ApiGroupPrefix = "/api";
|
||||
private const string SettingsPath = "/settings";
|
||||
private const string SettingsOpenApiTag = "settings";
|
||||
private const string InvalidBodyDetail = "Тело запроса должно быть JSON-объектом";
|
||||
|
||||
/// <summary>
|
||||
/// Регистрирует GET/PATCH /api/settings.
|
||||
/// </summary>
|
||||
/// <param name="app">Построитель маршрутов приложения.</param>
|
||||
/// <returns>Построитель маршрутов для цепочки вызовов.</returns>
|
||||
public static IEndpointRouteBuilder MapSettingsEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup(ApiGroupPrefix).WithTags(SettingsOpenApiTag);
|
||||
|
||||
group.MapGet(SettingsPath, GetSettingsAsync);
|
||||
group.MapPatch(SettingsPath, PatchSettingsAsync);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
// GET /api/settings: публичный снимок настроек текущего тенанта.
|
||||
private static async Task<IResult> GetSettingsAsync(HttpContext context, CancellationToken ct)
|
||||
{
|
||||
if (!context.HasUser())
|
||||
{
|
||||
return EndpointResults.Unauthorized(AuthHelpers.UnauthorizedDetail);
|
||||
}
|
||||
|
||||
SettingsService settingsService = context.RequestServices.GetRequiredService<SettingsService>();
|
||||
return Results.Ok(await settingsService.GetPublicAsync(ct));
|
||||
}
|
||||
|
||||
// PATCH /api/settings: частичное обновление настроек; ответ — полный снимок после применения.
|
||||
private static async Task<IResult> PatchSettingsAsync(HttpContext context, CancellationToken ct)
|
||||
{
|
||||
if (!context.HasUser())
|
||||
{
|
||||
return EndpointResults.Unauthorized(AuthHelpers.UnauthorizedDetail);
|
||||
}
|
||||
|
||||
Dictionary<string, JsonElement>? body;
|
||||
try
|
||||
{
|
||||
body = await JsonSerializer.DeserializeAsync<Dictionary<string, JsonElement>>(
|
||||
context.Request.Body,
|
||||
options: null,
|
||||
cancellationToken: ct);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return EndpointResults.BadRequest(InvalidBodyDetail);
|
||||
}
|
||||
|
||||
if (body is null)
|
||||
{
|
||||
return EndpointResults.BadRequest(InvalidBodyDetail);
|
||||
}
|
||||
|
||||
SettingsService settingsService = context.RequestServices.GetRequiredService<SettingsService>();
|
||||
PublicSettingsDto result = await settingsService.ApplyPatchAsync(body, ct);
|
||||
|
||||
await AuditAppender.AppendTenantAsync(context, AuditEvents.SettingsUpdated, new { fields = body.Keys }, ct);
|
||||
|
||||
if (ShouldScheduleRatesRefresh(body))
|
||||
{
|
||||
context.RequestServices.GetRequiredService<RatesRefreshScheduler>().Schedule();
|
||||
}
|
||||
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static bool ShouldScheduleRatesRefresh(Dictionary<string, JsonElement> body)
|
||||
{
|
||||
if (!body.TryGetValue(SettingsKeys.RateSource, out JsonElement element))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => !string.IsNullOrEmpty(element.GetString()),
|
||||
JsonValueKind.Null => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user