Почистить комментарии от упоминаний процесса

Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны
ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками
на процесс удалены; то же в .proto. Правила обновлены в
docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
Rustam Khalimov
2026-09-11 13:39:39 +03:00
parent 5f5538d33b
commit b053d58335
902 changed files with 3902 additions and 12074 deletions
@@ -9,19 +9,8 @@ using Deal.Grpc.Hosting.Services;
namespace Deal.Grpc.Hosting.Interceptors;
/// <summary>
/// Access-лог RPC Deal-сервисов (Ruling 7, план Task 14; общий шаблон трёх сервисов — C31): каждый
/// вызов (кроме gRPC-health) — одна структурированная строка «метод → статус за N мс».
/// Access-лог RPC Deal-сервисов
/// </summary>
/// <remarks>
/// Регистрируется ПЕРВЫМ в цепочке AddGrpc (до ServiceTokenInterceptor): логируются и отклонённые
/// вызовы (401) — access-лог должен видеть отказы. Значения запросов не логируются (в RPC — тексты/
/// промпты/ключи), секреты не пишутся (Ruling 13). gRPC-health (docker healthcheck ~5 с) пропускается —
/// иначе лог был бы зашумлён инфраструктурными пробами.
/// Access-лог ведётся для всех видов RPC (unary/клиентский/серверный/дуплексный стриминг): каждый
/// handler-метод исполняется через общий <see cref="LogAsync"/>. «Прочие» сбои реализации (не
/// отмена и не RpcException) логируются как Unknown и переводятся в RpcException — мимо лога они
/// больше не уходят (замечание code-review).
/// </remarks>
public sealed class RpcCallLoggingInterceptor : Interceptor
{
// Префикс методов стандартного gRPC-health — не логируется (инфраструктурный liveness).
@@ -35,7 +24,7 @@ public sealed class RpcCallLoggingInterceptor : Interceptor
/// <summary>
/// Создаёт интерцептор access-лога gRPC-вызовов.
/// </summary>
/// <param name="logger">Логгер (Serilog, Ruling 7).</param>
/// <param name="logger">Логгер.</param>
public RpcCallLoggingInterceptor(ILogger<RpcCallLoggingInterceptor> logger)
{
ArgumentNullException.ThrowIfNull(logger);
@@ -43,7 +32,7 @@ public sealed class RpcCallLoggingInterceptor : Interceptor
}
/// <summary>
/// Логирует unary-RPC: время вызова и итоговый gRPC-статус (успех либо статус исключения).
/// Логирует unary-RPC
/// </summary>
public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
@@ -52,7 +41,7 @@ public sealed class RpcCallLoggingInterceptor : Interceptor
=> LogAsync(context, () => continuation(request, context));
/// <summary>
/// Логирует client-streaming-RPC: access-строка пишется после завершения потока/вызова.
/// Логирует client-streaming-RPC
/// </summary>
public override Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(
IAsyncStreamReader<TRequest> requestStream,
@@ -61,7 +50,7 @@ public sealed class RpcCallLoggingInterceptor : Interceptor
=> LogAsync(context, () => continuation(requestStream, context));
/// <summary>
/// Логирует server-streaming-RPC: access-строка пишется после завершения потока/вызова.
/// Логирует server-streaming-RPC
/// </summary>
public override Task ServerStreamingServerHandler<TRequest, TResponse>(
TRequest request,
@@ -71,7 +60,7 @@ public sealed class RpcCallLoggingInterceptor : Interceptor
=> LogAsync(context, () => continuation(request, responseStream, context));
/// <summary>
/// Логирует дуплексный RPC: access-строка пишется после завершения потока/вызова.
/// Логирует дуплексный RPC
/// </summary>
public override Task DuplexStreamingServerHandler<TRequest, TResponse>(
IAsyncStreamReader<TRequest> requestStream,
@@ -10,44 +10,26 @@ using Deal.Grpc.Hosting.Services;
namespace Deal.Grpc.Hosting.Interceptors;
/// <summary>
/// Серверный интерцептор service-token (Ruling 1; общий шаблон трёх Deal-сервисов — C31).
///
/// Каждый RPC Deal-сервиса обязан нести gRPC-metadata «service-token», равный ожидаемому значению
/// из env DEAL_SERVICE_TOKEN (общий токен сервисов в compose, Ruling 12). Отсутствие или
/// несовпадение токена — отказ UNAUTHENTICATED до вызова метода сервиса. Стандартный
/// grpc.health.v1.Health токеном НЕ проверяется: это liveness инфраструктуры (docker healthcheck,
/// Ruling 12), данных тенантов он не отдаёт.
///
/// Fail-closed (замечание ревью Task 2 учтено): если DEAL_SERVICE_TOKEN не задан/пуст — любой
/// Deal-RPC отклоняется всегда. Явный гард обязателен: сравнение строк без него пропустило бы
/// запрос с пустым значением metadata («» == «»), а env-провайдер конфигурации возвращает пустую
/// строку вместо null для незаданного ключа.
///
/// Проверка выполняется для ВСЕХ видов RPC (unary/клиентский/серверный/дуплексный стриминг):
/// метод <see cref="EnsureAuthorized"/> вызывается из каждого handler-а (замечание code-review).
/// Серверный интерцептор service-token.
/// </summary>
public sealed class ServiceTokenInterceptor : Interceptor
{
/// <summary>
/// Ключ gRPC-metadata с токеном сервиса (контракт — README src/contracts).
/// Ключ gRPC-metadata с токеном сервиса
/// </summary>
public const string ServiceTokenMetadataKey = "service-token";
// Префикс методов стандартного gRPC-health, освобождённых от проверки токена.
private const string HealthMethodPrefix = "/grpc.health.v1.Health/";
// Env-ключ ожидаемого токена (только env; ключи/секреты не логируются — Ruling 13).
private const string ServiceTokenEnvKey = "DEAL_SERVICE_TOKEN";
// Деталь отказа — общий текст для трёх сервисов этапа (шаблон T2/T3/T4).
private const string RejectionDetail = "service-token отсутствует или неверен";
private readonly byte[] _expectedTokenBytes;
/// <summary>
/// Создаёт интерцептор. Ожидаемый токен читается из конфигурации (env DEAL_SERVICE_TOKEN)
/// в момент старта хоста; смена токена требует рестарта (как остальной env-конфиг). Токен
/// хранится в UTF-8-байтах для constant-time сравнения (<see cref="CryptographicOperations"/>).
/// Создаёт интерцептор.
/// </summary>
/// <param name="configuration">Конфигурация хоста (env-провайдер WebApplicationBuilder).</param>
public ServiceTokenInterceptor(IConfiguration configuration)
@@ -8,21 +8,8 @@ using Deal.Grpc.Hosting.Services;
namespace Deal.Grpc.Hosting.Models;
/// <summary>
/// Загруженный набор сертификатов mTLS внутреннего gRPC (Ruling 6, план Task 13; общий шаблон — C31).
/// Загруженный набор сертификатов mTLS внутреннего gRPC.
/// </summary>
/// <remarks>
/// Создаётся один раз на старте процесса, когда <see cref="MtlsOptions.Enabled"/>=true, из файлов
/// deploy/certs (генерация — scripts/mtls-certs.sh); при выключенном флаге <see cref="Load"/> возвращает
/// null — процесс остаётся на plaintext + service-token (Ruling 2 этапа 6). Экземпляр живёт до конца
/// процесса: сертификаты держат Kestrel (серверный) и исходящие каналы процессов с клиентской ролью
/// (клиентский), поэтому IDisposable сознательно нет — преждевременный Dispose сломал бы живые
/// соединения. Fail-fast: при включённом флаге любой пустой/битый путь или пароль —
/// <see cref="InvalidOperationException"/> на старте.
///
/// Проверка второй стороны — цепочка на нашу CA (CustomRootTrust, без revocation): dev-CA не в системном
/// хранилище, поэтому стандартная проверка доверия дала бы RemoteCertificateChainErrors и без кастомного
/// билда цепочки каждое соединение отвергалось бы.
/// </remarks>
public sealed class MtlsCertificates
{
// Роль в сообщениях об ошибках: CA-сертификат (проверка второй стороны).
@@ -45,23 +32,22 @@ public sealed class MtlsCertificates
}
/// <summary>
/// CA-сертификат из CaPem: корень доверия для проверки второй стороны.
/// CA-сертификат из CaPem
/// </summary>
public X509Certificate2 CaCertificate { get; }
/// <summary>
/// Серверный сертификат процесса из PFX (подпись своего Kestrel-gRPC-эндпоинта).
/// Серверный сертификат процесса из PFX
/// </summary>
public X509Certificate2 ServerCertificate { get; }
/// <summary>
/// Клиентский сертификат из PFX (подпись исходящих каналов, общий deal-client).
/// Клиентский сертификат из PFX
/// </summary>
public X509Certificate2 ClientCertificate { get; }
/// <summary>
/// Загружает сертификаты из <paramref name="options"/>: null при выключенном флаге (режим plaintext),
/// иначе — CA + серверный + клиентский с fail-fast на битые пути/пароли.
/// Загружает сертификаты из <paramref name="options"/>
/// </summary>
/// <param name="options">Опции mTLS (env DEAL_MTLS_*).</param>
/// <returns>Набор сертификатов либо null (флаг выключен).</returns>
@@ -81,9 +67,7 @@ public sealed class MtlsCertificates
}
/// <summary>
/// Серверная проверка клиентского сертификата для Kestrel (ClientCertificateValidation): сертификат
/// обязан быть подписан нашей CA (цепочка до CaPem). Стандартные ошибки цепочки (наша CA вне системного
/// хранилища) пересобираются кастомным билдом; иные ошибки (нет сертификата/недоступен) — отказ.
/// Серверная проверка клиентского сертификата для Kestrel
/// </summary>
/// <param name="certificate">Клиентский сертификат из рукопожатия (null — RequireCertificate не выполнен).</param>
/// <param name="chain">Цепочка стандартной проверки (игнорируется — пересобирается на нашу CA).</param>
@@ -112,8 +96,7 @@ public sealed class MtlsCertificates
}
/// <summary>
/// Создаёт HTTP/2-хендлер исходящего канала: клиентский сертификат + проверка CA сервера
/// (используют процессы с исходящими gRPC-каналами — общий шаблон).
/// Создаёт HTTP/2-хендлер исходящего канала
/// </summary>
/// <returns>Новый SocketsHttpHandler (владелец — создатель; канал GrpcChannel закроет его вместе с собой).</returns>
public SocketsHttpHandler CreateClientHttpHandler()
@@ -6,26 +6,17 @@ using Deal.Grpc.Hosting.Services;
namespace Deal.Grpc.Hosting.Options;
/// <summary>
/// Конфигурация mTLS-транспорта внутреннего gRPC (Ruling 6, план Task 13; общий шаблон — C31).
/// Конфигурация mTLS-транспорта внутреннего gRPC.
/// </summary>
/// <remarks>
/// Только env (Ruling 13: секреты/пути сертификатов не читаются из appsettings): флаг
/// <c>DEAL_MTLS_ENABLED</c> и пути/пароли <c>DEAL_MTLS_*</c> из Ruling 6. Dev-дефолт — выключено
/// (<see cref="Enabled"/> = false): процесс остаётся на plaintext + service-token (Ruling 2 этапа 6);
/// PROD включает флаг env из compose-prod (Task 14; файлы монтируются из deploy/certs/, генерация —
/// scripts/mtls-certs.sh). Env-схема общая для процессов Deal (Ruling 6): серверный PFX — для своего
/// Kestrel-gRPC; клиентский PFX задаётся единообразно и используется процессами с исходящими
/// каналами (общий deal-client); CA — для проверки второй стороны.
/// </remarks>
public sealed class MtlsOptions
{
/// <summary>
/// Env-ключ флага: 1/true включает mTLS (как DEAL_DEMO=1).
/// Env-ключ флага: 1/true включает mTLS
/// </summary>
public const string EnabledEnvKey = "DEAL_MTLS_ENABLED";
/// <summary>
/// Env-ключ пути к PFX серверного сертификата процесса (Kestrel-gRPC).
/// Env-ключ пути к PFX серверного сертификата процесса
/// </summary>
public const string ServerCertPfxEnvKey = "DEAL_MTLS_SERVER_CERT_PFX";
@@ -35,7 +26,7 @@ public sealed class MtlsOptions
public const string ServerCertPasswordEnvKey = "DEAL_MTLS_SERVER_CERT_PASSWORD";
/// <summary>
/// Env-ключ пути к PFX клиентского сертификата (общий deal-client исходящих каналов).
/// Env-ключ пути к PFX клиентского сертификата
/// </summary>
public const string ClientCertPfxEnvKey = "DEAL_MTLS_CLIENT_CERT_PFX";
@@ -45,7 +36,7 @@ public sealed class MtlsOptions
public const string ClientCertPasswordEnvKey = "DEAL_MTLS_CLIENT_CERT_PASSWORD";
/// <summary>
/// Env-ключ пути к PEM dev-CA (проверка сертификата второй стороны).
/// Env-ключ пути к PEM dev-CA
/// </summary>
public const string CaPemEnvKey = "DEAL_MTLS_CA_PEM";
@@ -55,32 +46,32 @@ public sealed class MtlsOptions
public bool Enabled { get; init; }
/// <summary>
/// Путь к PFX серверного сертификата процесса (см. <see cref="ServerCertPfxEnvKey"/>).
/// Путь к PFX серверного сертификата процесса
/// </summary>
public string ServerCertPfx { get; init; } = string.Empty;
/// <summary>
/// Пароль серверного PFX (см. <see cref="ServerCertPasswordEnvKey"/>).
/// Пароль серверного PFX
/// </summary>
public string ServerCertPassword { get; init; } = string.Empty;
/// <summary>
/// Путь к PFX клиентского сертификата (см. <see cref="ClientCertPfxEnvKey"/>).
/// Путь к PFX клиентского сертификата
/// </summary>
public string ClientCertPfx { get; init; } = string.Empty;
/// <summary>
/// Пароль клиентского PFX (см. <see cref="ClientCertPasswordEnvKey"/>).
/// Пароль клиентского PFX
/// </summary>
public string ClientCertPassword { get; init; } = string.Empty;
/// <summary>
/// Путь к PEM-файлу dev-CA (см. <see cref="CaPemEnvKey"/>).
/// Путь к PEM-файлу dev-CA
/// </summary>
public string CaPem { get; init; } = string.Empty;
/// <summary>
/// Читает опции из конфигурации хоста (env-ключи DEAL_MTLS_*, только env — Ruling 13).
/// Читает опции из конфигурации хоста.
/// </summary>
/// <param name="configuration">Конфигурация хоста (env-провайдер WebApplicationBuilder).</param>
/// <returns>Опции mTLS (флаг выключен — остальные поля пустые).</returns>
@@ -99,7 +90,7 @@ public sealed class MtlsOptions
}
/// <summary>
/// Разбирает значение флага DEAL_MTLS_ENABLED: «1»/«true» (без учёта регистра) — включено.
/// Разбирает значение флага DEAL_MTLS_ENABLED
/// </summary>
/// <param name="rawValue">Сырое значение env (null/пусто — выключено).</param>
public static bool IsEnabled(string? rawValue)
@@ -11,23 +11,8 @@ using Deal.Grpc.Hosting.Options;
namespace Deal.Grpc.Hosting.Services;
/// <summary>
/// Serilog-конфигурация процесса Deal-сервиса (Ruling 7/9, план Task 14; общий шаблон — C31).
///
/// Консоль — JSON в prod-стиле (CompactJsonFormatter: одна JSON-строка на событие, поля @t/@mt/@l —
/// парсинг Loki/Promtail) либо текст в Development; плюс rolling-файл data/logs/deal-&lt;процесс&gt;.json
/// под ContentRoot (/app в контейнере). Уровень/каталог переопределяются env: DEAL_LOG_LEVEL,
/// DEAL_LOGS_DIR.
/// Serilog-конфигурация процесса Deal-сервиса.
/// </summary>
/// <remarks>
/// Конфигурация кодом, а не секцией appsettings: у сервиса appsettings.json нет (весь конфиг — env,
/// Ruling 13), поэтому единый код-набор с env-переопределениями не расходится между процессами.
/// Секреты не логируются (Ruling 13); OTel/метрики в этапе 7 не добавляются (Ruling 7) — стек:
/// Serilog-логи → docker-логи → Promtail → Loki → Grafana.
///
/// Вызов — из Program.cs процесса (entry point): <c>DealLogging.Configure(builder, "имя_процесса")</c>
/// ДО <c>builder.Build()</c>. Интеграционные тесты поднимают хост через *ServiceHost.Create БЕЗ этого
/// вызова (логирование — забота production-точки входа), поэтому тесты не пишут файлы-логи.
/// </remarks>
public static class DealLogging
{
// Env-ключ минимального уровня Serilog (Debug/Information/Warning/Error; дефолт Information).
@@ -56,8 +41,7 @@ public static class DealLogging
private const LogEventLevel DefaultMinimumLevel = LogEventLevel.Information;
/// <summary>
/// Подключает Serilog к хосту (builder.Host.UseSerilog). Регистрация отложенная: конфигурация
/// логгера применяется при builder.Build(), когда среда/конфигурация (env) уже собраны.
/// Подключает Serilog к хосту
/// </summary>
/// <param name="builder">Билдер WebApplication процесса (до Build).</param>
/// <param name="processName">Имя процесса для имени файла-лога (telegram/ai/ml/…).</param>
@@ -10,33 +10,17 @@ using Deal.Grpc.Hosting.Options;
namespace Deal.Grpc.Hosting.Services;
/// <summary>
/// Общая настройка метрик Deal-сервисов (этап 12, пакет A): OpenTelemetry → экспортёр Prometheus,
/// эндпоинт <c>/metrics</c> в отдельном HTTP/1.1 Kestrel-эндпоинте (порт 9464 по умолчанию).
/// Общая настройка метрик Deal-сервисов
/// </summary>
/// <remarks>
/// <para>
/// gRPC-сервисы слушают HTTP/2 (см. <see cref="GrpcServer.ConfigureKestrelHttp2Endpoint"/>), а
/// Prometheus scrape'ит обычным HTTP/1.1-запросом GET — поэтому метрики вынесены на отдельный
/// Kestrel-эндпоинт с <see cref="HttpProtocols.Http1"/>: тот же процесс, тот же DI, но отдельный порт.
/// Порт не публикуется наружу — scrape идёт внутри compose-сети от сервиса <c>prometheus</c>.
/// </para>
/// <para>
/// Сервисы вызывают ровно две строки: <see cref="AddDealMetrics"/> — на этапе сборки хоста (до
/// <c>builder.Build()</c>, обычно из <c>configureBuilder</c>-хука Program.cs), и
/// <see cref="MapDealMetrics"/> — после сборки. Инструментация (входящие ASP.NET Core/gRPC,
/// исходящие HTTP/gRPC) даёт метрики RPS/латентности/ошибок без ручного кода; прикладные метрики
/// (токены, аудит, очереди) добавляет ядро своим meter'ом <see cref="MeterName"/>.
/// </para>
/// </remarks>
public static class DealMetricsHosting
{
/// <summary>
/// Имя meter'а прикладных метрик Deal (общий префикс с ядром: <c>deal.*</c>).
/// Имя meter'а прикладных метрик Deal
/// </summary>
public const string MeterName = "Deal";
/// <summary>
/// Порт эндпоинта <c>/metrics</c> по умолчанию (конвенция OpenTelemetry Prometheus).
/// Порт эндпоинта <c>/metrics</c> по умолчанию
/// </summary>
public const int DefaultMetricsPort = 9464;
@@ -44,9 +28,7 @@ public static class DealMetricsHosting
private const string MetricsPortEnvKey = "METRICS_PORT";
/// <summary>
/// Порт эндпоинта метрик: env <c>METRICS_PORT</c> (заданное нечисловое значение игнорируется),
/// иначе <see cref="DefaultMetricsPort"/>. Локальный запуск нескольких процессов на хосте без
/// compose требует разных значений (в compose порты контейнеров изолированы).
/// Порт эндпоинта метрик
/// </summary>
/// <param name="defaultPort">Дефолтный порт (обычно <see cref="DefaultMetricsPort"/>).</param>
/// <returns>Порт HTTP/1.1-эндпоинта метрик.</returns>
@@ -56,8 +38,7 @@ public static class DealMetricsHosting
: defaultPort;
/// <summary>
/// Регистрирует OTel-метрики и Kestrel-эндпоинт метрик (HTTP/1.1, 0.0.0.0:<paramref name="metricsPort"/>).
/// Вызывать до <c>builder.Build()</c>.
/// Регистрирует OTel-метрики и Kestrel-эндпоинт метрик
/// </summary>
/// <param name="builder">Билдер хоста сервиса.</param>
/// <param name="metricsPort">Порт HTTP/1.1-эндпоинта метрик.</param>
@@ -86,7 +67,7 @@ public static class DealMetricsHosting
}
/// <summary>
/// Мапит эндпоинт <c>/metrics</c> (формат Prometheus). Вызывать после <c>builder.Build()</c>.
/// Мапит эндпоинт <c>/metrics</c>
/// </summary>
/// <param name="app">Собранное приложение сервиса.</param>
public static void MapDealMetrics(WebApplication app)
@@ -5,9 +5,7 @@ using Deal.Grpc.Hosting.Options;
namespace Deal.Grpc.Hosting.Services;
/// <summary>
/// Общие стартовые проверки/разбор env для Program.cs Deal-сервисов (C31): порт Kestrel
/// (GRPC_PORT → PORT → дефолт), окружение ASP.NET Core и fail-closed mTLS в Production
/// (замечание code-review: отсутствие/опечатка DEAL_MTLS_ENABLED не должны давать «тихого» plaintext).
/// Общие стартовые проверки/разбор env для Program.cs Deal-сервисов
/// </summary>
public static class GrpcHostEnvironment
{
@@ -28,8 +26,7 @@ public static class GrpcHostEnvironment
"Production требует mTLS: задайте DEAL_MTLS_ENABLED=1 и env DEAL_MTLS_* (сертификаты deploy/certs, генерация — scripts/mtls-certs.sh)";
/// <summary>
/// Порт Kestrel процесса: env GRPC_PORT (контейнер), затем PORT (общий env хостинг-платформ),
/// иначе дефолт сервиса (Ruling 12, compose.dev.yml).
/// Порт Kestrel процесса
/// </summary>
/// <param name="defaultPort">Дефолтный порт сервиса.</param>
public static int ResolveGrpcPort(int defaultPort)
@@ -38,15 +35,14 @@ public static class GrpcHostEnvironment
?? defaultPort;
/// <summary>
/// Парсит порт из env-строки; пустое/нечисловое значение — null (перебор следующего источника).
/// Парсит порт из env-строки; пустое/нечисловое значение — null
/// </summary>
/// <param name="rawValue">Сырое значение env.</param>
public static int? ParsePort(string? rawValue)
=> int.TryParse(rawValue, out int parsedPort) ? parsedPort : null;
/// <summary>
/// True — окружение Production (ASPNETCORE_ENVIRONMENT; незаданный env Production-ом не считается —
/// dev-локальный запуск без переменной остаётся на plaintext, как раньше).
/// True — окружение Production
/// </summary>
public static bool IsProductionEnvironment()
=> string.Equals(
@@ -55,9 +51,7 @@ public static class GrpcHostEnvironment
StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Fail-closed-гард транспорта: при ASPNETCORE_ENVIRONMENT=Production и выключенном mTLS —
/// отказ на старте с понятным текстом (Development и прочие не-prod окружения: plaintext
/// + service-token допустимы, Ruling 2). Проверять после создания хоста (env уже собраны).
/// Fail-closed-гард транспорта
/// </summary>
/// <param name="mtlsOptions">Опции mTLS процесса (из env DEAL_MTLS_*).</param>
/// <exception cref="InvalidOperationException">Production без mTLS.</exception>
@@ -12,9 +12,7 @@ using Deal.Grpc.Hosting.Options;
namespace Deal.Grpc.Hosting.Services;
/// <summary>
/// Общие серверные блоки gRPC-хостов Deal-сервисов (C31): mTLS-набор, Kestrel HTTP/2-эндпоинт,
/// AddGrpc с интерцепторами и gRPC-health. Host-фабрики сервисов (TelegramServiceHost/AiServiceHost/
/// MlServiceHost) собирают эти блоки здесь один раз, затем регистрируют свою доменную логику.
/// Общие серверные блоки gRPC-хостов Deal-сервисов
/// </summary>
public static class GrpcServer
{
@@ -22,16 +20,12 @@ public static class GrpcServer
private const string ReadyHealthCheckName = "ready";
/// <summary>
/// Потолок входящего gRPC-сообщения (серверный лимит на границе, замечание code-review; 4 МБ —
/// запросы контрактов сервисов помещаются с запасом).
/// Потолок входящего gRPC-сообщения
/// </summary>
public const int DefaultMaxReceiveMessageSize = 4 * 1024 * 1024;
/// <summary>
/// Загружает сертификаты mTLS из env (DEAL_MTLS_*, Ruling 6/Task 13) и регистрирует набор
/// в DI: null при выключенном флаге (plaintext + service-token, dev); при включённом —
/// fail-fast на битые пути/пароли. Возвращённый экземпляр используют Kestrel и (в процессах
/// с клиентской ролью) исходящие каналы.
/// Загружает сертификаты mTLS из env и регистрирует набор в DI
/// </summary>
/// <param name="builder">Билдер хоста (конфигурация env + DI).</param>
/// <returns>Набор сертификатов либо null (mTLS выключен).</returns>
@@ -50,9 +44,7 @@ public static class GrpcServer
}
/// <summary>
/// Настраивает единственный Kestrel-эндпоинт HTTP/2 на 0.0.0.0:grpcPort: plaintext (dev, Ruling 2)
/// либо mTLS при переданном наборе сертификатов (серверный сертификат + требование клиентского
/// с проверкой через нашу CA, Ruling 6).
/// Настраивает единственный Kestrel-эндпоинт HTTP/2 на 0.0.0.0:grpcPort
/// </summary>
/// <param name="builder">Билдер хоста (WebHost для ConfigureKestrel).</param>
/// <param name="grpcPort">TCP-порт Kestrel.</param>
@@ -82,10 +74,7 @@ public static class GrpcServer
}
/// <summary>
/// Регистрирует AddGrpc с общей серверной обвязкой: access-лог ПЕРВЫМ (логирует и отклонённые
/// вызовы), затем проверка service-token (Ruling 1) на каждом Deal-RPC; grpc.health.v1.Health
/// освобождён от токена и access-лога (см. ServiceTokenInterceptor/RpcCallLoggingInterceptor).
/// Плюс потолок входящего сообщения <see cref="DefaultMaxReceiveMessageSize"/>.
/// Регистрирует AddGrpc с общей серверной обвязкой
/// </summary>
/// <param name="services">DI сервисов хоста.</param>
public static IServiceCollection AddDealGrpcServer(this IServiceCollection services)
@@ -101,8 +90,7 @@ public static class GrpcServer
}
/// <summary>
/// Регистрирует стандартный gRPC-health (Grpc.HealthCheck): healthcheck контейнера (Ruling 12)
/// с явной проверкой <c>ready</c> — без неё health-сервис отвечает UNKNOWN, а не SERVING.
/// Регистрирует стандартный gRPC-health
/// </summary>
/// <param name="services">DI сервисов хоста.</param>
/// <param name="readyDetail">Текст готовности проверки (имя хоста в логах healthcheck).</param>