Дочистить код-стайл: var встроенных типов, приватные доки, доки интерфейсам
Гейт csharp_style_var_for_built_in_types=false:warning (ломает сборку), остаток выправлен dotnet format IDE0008 по 5 sln (только встроенные типы). Понижено 12 новых XML-доков private/internal, добавлены <summary> членам IContainerRules и ITenantContext, 3 англоязычных комментария переведены на русский.
This commit is contained in:
+2
-1
@@ -30,7 +30,8 @@ dotnet_style_qualification_for_method = false:warning
|
|||||||
dotnet_style_qualification_for_event = false:warning
|
dotnet_style_qualification_for_event = false:warning
|
||||||
|
|
||||||
# Члены
|
# Члены
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
# var — запрещён для встроенных типов (ломает сборку), для очевидных/прочих — silent (§4 код-стайла)
|
||||||
|
csharp_style_var_for_built_in_types = false:warning
|
||||||
csharp_style_var_when_type_is_apparent = false:silent
|
csharp_style_var_when_type_is_apparent = false:silent
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
|
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ public static class AuthEndpoints
|
|||||||
HttpContext context,
|
HttpContext context,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var cookieName = cookieOptions.Value.Name;
|
string cookieName = cookieOptions.Value.Name;
|
||||||
var rawToken = context.Request.Cookies[cookieName];
|
string? rawToken = context.Request.Cookies[cookieName];
|
||||||
// Пользователь разрешённой сессии — до её удаления (SessionMiddleware наполнил Items на старте запроса).
|
// Пользователь разрешённой сессии — до её удаления (SessionMiddleware наполнил Items на старте запроса).
|
||||||
CurrentUser? user = context.GetCurrentUser();
|
CurrentUser? user = context.GetCurrentUser();
|
||||||
var logout = await authService.LogoutAsync(rawToken, ct);
|
var logout = await authService.LogoutAsync(rawToken, ct);
|
||||||
@@ -166,7 +166,7 @@ public static class AuthEndpoints
|
|||||||
var result = await authService.ChangePasswordAsync(user.Login, body.OldPassword, body.NewPassword, ct);
|
var result = await authService.ChangePasswordAsync(user.Login, body.OldPassword, body.NewPassword, ct);
|
||||||
if (!result.Ok || result.NewToken is null)
|
if (!result.Ok || result.NewToken is null)
|
||||||
{
|
{
|
||||||
var detail = result.Error == ChangePasswordResultDto.ErrorTooShort
|
string detail = result.Error == ChangePasswordResultDto.ErrorTooShort
|
||||||
? PasswordTooShortDetail
|
? PasswordTooShortDetail
|
||||||
: WrongOldPasswordDetail;
|
: WrongOldPasswordDetail;
|
||||||
return EndpointResults.BadRequest(detail);
|
return EndpointResults.BadRequest(detail);
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ public static class OperatorAuthEndpoints
|
|||||||
HttpContext context,
|
HttpContext context,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var cookieName = cookieOptions.Value.Name;
|
string cookieName = cookieOptions.Value.Name;
|
||||||
var rawToken = context.Request.Cookies[cookieName];
|
string? rawToken = context.Request.Cookies[cookieName];
|
||||||
// Оператор разрешённой сессии — до её удаления (OperatorSessionMiddleware наполнил Items).
|
// Оператор разрешённой сессии — до её удаления (OperatorSessionMiddleware наполнил Items).
|
||||||
CurrentOperator? operatorIdentity = context.GetCurrentOperator();
|
CurrentOperator? operatorIdentity = context.GetCurrentOperator();
|
||||||
await operatorAuthService.LogoutAsync(rawToken, ct);
|
await operatorAuthService.LogoutAsync(rawToken, ct);
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ public sealed class OperatorBootstrapHostedService(
|
|||||||
await using var scope = scopeFactory.CreateAsyncScope();
|
await using var scope = scopeFactory.CreateAsyncScope();
|
||||||
var operatorBootstrapService = scope.ServiceProvider.GetRequiredService<OperatorBootstrapService>();
|
var operatorBootstrapService = scope.ServiceProvider.GetRequiredService<OperatorBootstrapService>();
|
||||||
|
|
||||||
var login = configuration[OperatorBootstrapService.LoginEnvKey];
|
string? login = configuration[OperatorBootstrapService.LoginEnvKey];
|
||||||
var password = configuration[OperatorBootstrapService.PasswordEnvKey];
|
string? password = configuration[OperatorBootstrapService.PasswordEnvKey];
|
||||||
bool hasLogin = !string.IsNullOrWhiteSpace(login);
|
bool hasLogin = !string.IsNullOrWhiteSpace(login);
|
||||||
bool hasPassword = !string.IsNullOrWhiteSpace(password);
|
bool hasPassword = !string.IsNullOrWhiteSpace(password);
|
||||||
bool allowDevelopmentDefaults = environment.IsDevelopment();
|
bool allowDevelopmentDefaults = environment.IsDevelopment();
|
||||||
@@ -47,7 +47,7 @@ public sealed class OperatorBootstrapHostedService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ensuredLogin = await operatorBootstrapService.EnsureOperatorAsync(
|
string? ensuredLogin = await operatorBootstrapService.EnsureOperatorAsync(
|
||||||
login, password, allowDevelopmentDefaults, ct);
|
login, password, allowDevelopmentDefaults, ct);
|
||||||
if (ensuredLogin is not null)
|
if (ensuredLogin is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ public sealed class TenantBootstrapService(IServiceScopeFactory scopeFactory) :
|
|||||||
var authStore = scope.ServiceProvider.GetRequiredService<IAuthStore>();
|
var authStore = scope.ServiceProvider.GetRequiredService<IAuthStore>();
|
||||||
var passwordHasher = scope.ServiceProvider.GetRequiredService<IPasswordHasher>();
|
var passwordHasher = scope.ServiceProvider.GetRequiredService<IPasswordHasher>();
|
||||||
|
|
||||||
var login = NormalizeLogin(configuration[BootstrapLoginEnvKey] ?? DefaultAdminLogin);
|
string login = NormalizeLogin(configuration[BootstrapLoginEnvKey] ?? DefaultAdminLogin);
|
||||||
var password = configuration[BootstrapPasswordEnvKey] ?? DefaultAdminPassword;
|
string password = configuration[BootstrapPasswordEnvKey] ?? DefaultAdminPassword;
|
||||||
var environment = scope.ServiceProvider.GetRequiredService<IHostEnvironment>();
|
var environment = scope.ServiceProvider.GetRequiredService<IHostEnvironment>();
|
||||||
|
|
||||||
var seedDefaultTenant = environment.IsDevelopment()
|
bool seedDefaultTenant = environment.IsDevelopment()
|
||||||
|| configuration[DefaultTenantBootstrapEnvKey] == DefaultTenantBootstrapEnabledValue;
|
|| configuration[DefaultTenantBootstrapEnvKey] == DefaultTenantBootstrapEnabledValue;
|
||||||
if (seedDefaultTenant)
|
if (seedDefaultTenant)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ public sealed class OperatorSessionMiddleware
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task InvokeAsync(HttpContext context)
|
public async Task InvokeAsync(HttpContext context)
|
||||||
{
|
{
|
||||||
var cookieName = _cookieOptions.CurrentValue.Name;
|
string cookieName = _cookieOptions.CurrentValue.Name;
|
||||||
if (context.Request.Cookies.TryGetValue(cookieName, out var rawToken)
|
if (context.Request.Cookies.TryGetValue(cookieName, out string? rawToken)
|
||||||
&& !string.IsNullOrWhiteSpace(rawToken))
|
&& !string.IsNullOrWhiteSpace(rawToken))
|
||||||
{
|
{
|
||||||
// OperatorAuthService scoped: создаём scope на запрос через RequestServices.
|
// OperatorAuthService scoped: создаём scope на запрос через RequestServices.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ public sealed class SessionMiddleware
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cookieName = _cookieOptions.CurrentValue.Name;
|
string cookieName = _cookieOptions.CurrentValue.Name;
|
||||||
if (context.Request.Cookies.TryGetValue(cookieName, out var rawToken)
|
if (context.Request.Cookies.TryGetValue(cookieName, out string? rawToken)
|
||||||
&& !string.IsNullOrWhiteSpace(rawToken))
|
&& !string.IsNullOrWhiteSpace(rawToken))
|
||||||
{
|
{
|
||||||
// AuthService scoped: создаём scope на запрос через RequestServices.
|
// AuthService scoped: создаём scope на запрос через RequestServices.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ DealMetricsHosting.AddDealMetrics(builder, metricsPort);
|
|||||||
|
|
||||||
// Строка подключения Postgres — ТОЛЬКО из конфигурации (env/appsettings): dev-пароль в коде отсутствует
|
// Строка подключения Postgres — ТОЛЬКО из конфигурации (env/appsettings): dev-пароль в коде отсутствует
|
||||||
// (Security review). Отсутствие строки = fail-fast на старте, а не тихий уход на несуществующую dev-БД.
|
// (Security review). Отсутствие строки = fail-fast на старте, а не тихий уход на несуществующую dev-БД.
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DealPostgres")
|
string connectionString = builder.Configuration.GetConnectionString("DealPostgres")
|
||||||
?? throw new InvalidOperationException("ConnectionStrings:DealPostgres не задан");
|
?? throw new InvalidOperationException("ConnectionStrings:DealPostgres не задан");
|
||||||
builder.Services.AddDbContext<DealDbContext>(options => options.UseNpgsql(connectionString));
|
builder.Services.AddDbContext<DealDbContext>(options => options.UseNpgsql(connectionString));
|
||||||
builder.Services.AddSingleton<ITenantContext, TenantContext>();
|
builder.Services.AddSingleton<ITenantContext, TenantContext>();
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using Grpc.Core;
|
|||||||
|
|
||||||
namespace Deal.Infrastructure.Integrations.Extensions;
|
namespace Deal.Infrastructure.Integrations.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения классификации gRPC-исключений клиентов автономных сервисов.
|
||||||
/// Расширения классификации gRPC-исключений клиентов автономных сервисов.
|
|
||||||
/// </summary>
|
|
||||||
internal static class RpcExceptionExtensions
|
internal static class RpcExceptionExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ using System.Net.Sockets;
|
|||||||
|
|
||||||
namespace Deal.Infrastructure.Integrations.Extensions;
|
namespace Deal.Infrastructure.Integrations.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения Uri для SSRF-гейта интеграций
|
||||||
/// Расширения <see cref="Uri"/> для SSRF-гейта интеграций
|
|
||||||
/// </summary>
|
|
||||||
internal static class UriExtensions
|
internal static class UriExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
+1
-3
@@ -2,9 +2,7 @@ using Deal.Infrastructure.Integrations.Storage.Options;
|
|||||||
|
|
||||||
namespace Deal.Infrastructure.Integrations.Storage.Extensions;
|
namespace Deal.Infrastructure.Integrations.Storage.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения MinioStorageOptions
|
||||||
/// Расширения <see cref="MinioStorageOptions"/>
|
|
||||||
/// </summary>
|
|
||||||
internal static class MinioStorageOptionsExtensions
|
internal static class MinioStorageOptionsExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Infrastructure.Integrations.Storage.Extensions;
|
namespace Deal.Infrastructure.Integrations.Storage.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения string для разбора конфигурационных значений.
|
||||||
/// Расширения <see cref="string"/> для разбора конфигурационных значений.
|
|
||||||
/// </summary>
|
|
||||||
internal static class StringExtensions
|
internal static class StringExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public static class TenantSchemaMigrator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string CreateSchemaSql(string schemaName)
|
public static string CreateSchemaSql(string schemaName)
|
||||||
{
|
{
|
||||||
var escaped = schemaName.Replace("\"", "\"\"");
|
string escaped = schemaName.Replace("\"", "\"\"");
|
||||||
return $"CREATE SCHEMA IF NOT EXISTS \"{escaped}\"";
|
return $"CREATE SCHEMA IF NOT EXISTS \"{escaped}\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public sealed class DealDbDesignTimeFactory : IDesignTimeDbContextFactory<DealDb
|
|||||||
{
|
{
|
||||||
public DealDbContext CreateDbContext(string[] args)
|
public DealDbContext CreateDbContext(string[] args)
|
||||||
{
|
{
|
||||||
var connectionString = Environment.GetEnvironmentVariable("DEAL_PG_CONNECTION")
|
string connectionString = Environment.GetEnvironmentVariable("DEAL_PG_CONNECTION")
|
||||||
?? "Host=localhost;Port=5433;Database=deal;Username=deal;Password=deal_dev_password";
|
?? "Host=localhost;Port=5433;Database=deal;Username=deal;Password=deal_dev_password";
|
||||||
var options = new DbContextOptionsBuilder<DealDbContext>()
|
var options = new DbContextOptionsBuilder<DealDbContext>()
|
||||||
.UseNpgsql(connectionString)
|
.UseNpgsql(connectionString)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public sealed class TenantDbDesignTimeFactory : IDesignTimeDbContextFactory<Tena
|
|||||||
{
|
{
|
||||||
public TenantDbContext CreateDbContext(string[] args)
|
public TenantDbContext CreateDbContext(string[] args)
|
||||||
{
|
{
|
||||||
var connectionString = Environment.GetEnvironmentVariable("DEAL_PG_CONNECTION")
|
string connectionString = Environment.GetEnvironmentVariable("DEAL_PG_CONNECTION")
|
||||||
?? "Host=localhost;Port=5433;Database=deal;Username=deal;Password=deal_dev_password";
|
?? "Host=localhost;Port=5433;Database=deal;Username=deal;Password=deal_dev_password";
|
||||||
var options = new DbContextOptionsBuilder<TenantDbContext>()
|
var options = new DbContextOptionsBuilder<TenantDbContext>()
|
||||||
.UseNpgsql(connectionString, npgsql => npgsql.MigrationsHistoryTable("__TenantMigrationsHistory"))
|
.UseNpgsql(connectionString, npgsql => npgsql.MigrationsHistoryTable("__TenantMigrationsHistory"))
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public sealed class TenantProvisioningService(ConnectionStringProvider connectio
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task ProvisionAsync(TenantId tenantId, CancellationToken ct)
|
public async Task ProvisionAsync(TenantId tenantId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var schemaName = tenantId.SchemaName;
|
string schemaName = tenantId.SchemaName;
|
||||||
var provisionLock = SchemaProvisionLocks.GetOrAdd(schemaName, static _ => new SemaphoreSlim(1, 1));
|
var provisionLock = SchemaProvisionLocks.GetOrAdd(schemaName, static _ => new SemaphoreSlim(1, 1));
|
||||||
await provisionLock.WaitAsync(ct);
|
await provisionLock.WaitAsync(ct);
|
||||||
try
|
try
|
||||||
@@ -63,7 +63,7 @@ public sealed class TenantProvisioningService(ConnectionStringProvider connectio
|
|||||||
{
|
{
|
||||||
// Строка уже с Search Path=tenant_<id> (ForSchemaDdl) — таблицы бессхемной модели TenantDbContext
|
// Строка уже с Search Path=tenant_<id> (ForSchemaDdl) — таблицы бессхемной модели TenantDbContext
|
||||||
// лягут в схему тенанта. DDL применяется мигратор-ролью при её наличии (см. ConnectionStringProvider).
|
// лягут в схему тенанта. DDL применяется мигратор-ролью при её наличии (см. ConnectionStringProvider).
|
||||||
var connectionString = connectionStringProvider.ForSchemaDdl(tenantId);
|
string connectionString = connectionStringProvider.ForSchemaDdl(tenantId);
|
||||||
var options = new DbContextOptionsBuilder<TenantDbContext>()
|
var options = new DbContextOptionsBuilder<TenantDbContext>()
|
||||||
.UseNpgsql(connectionString, npgsql => npgsql.MigrationsHistoryTable(TenantMigrationsHistoryTable, schemaName))
|
.UseNpgsql(connectionString, npgsql => npgsql.MigrationsHistoryTable(TenantMigrationsHistoryTable, schemaName))
|
||||||
.Options;
|
.Options;
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ public interface IContainerRules
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Mode { get; }
|
public string Mode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключевые слова
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyList<string> Keywords { get; }
|
public IReadOnlyList<string> Keywords { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Стек технологий
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyList<string> Stack { get; }
|
public IReadOnlyList<string> Stack { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using Deal.Modules.Kanban.Application.Models;
|
|||||||
|
|
||||||
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения бюджетной группы правил колонки.
|
||||||
/// Расширения бюджетной группы правил колонки.
|
|
||||||
/// </summary>
|
|
||||||
internal static class BudgetRangeDtoExtensions
|
internal static class BudgetRangeDtoExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения списков термов правил колонки.
|
||||||
/// Расширения списков термов правил колонки.
|
|
||||||
/// </summary>
|
|
||||||
internal static class TermListExtensions
|
internal static class TermListExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Modules.Kanban.Application.Extensions;
|
namespace Deal.Modules.Kanban.Application.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения символов для нормализации бюджетной валюты.
|
||||||
/// Расширения символов для нормализации бюджетной валюты.
|
|
||||||
/// </summary>
|
|
||||||
internal static class CharExtensions
|
internal static class CharExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Modules.Pipeline.Application.Parse;
|
namespace Deal.Modules.Pipeline.Application.Parse;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения кодовых точек для чистки текста сообщений.
|
||||||
/// Расширения кодовых точек для чистки текста сообщений.
|
|
||||||
/// </summary>
|
|
||||||
internal static class CodePointExtensions
|
internal static class CodePointExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Modules.Pipeline.Application.Parse;
|
namespace Deal.Modules.Pipeline.Application.Parse;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения строк для разбора текста сообщений.
|
||||||
/// Расширения строк для разбора текста сообщений.
|
|
||||||
/// </summary>
|
|
||||||
internal static class StringExtensions
|
internal static class StringExtensions
|
||||||
{
|
{
|
||||||
private static readonly string[] FooterHintsArray =
|
private static readonly string[] FooterHintsArray =
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public static class SettingsKeys
|
|||||||
|
|
||||||
public const string ExcludeTypes = "excludeTypes";
|
public const string ExcludeTypes = "excludeTypes";
|
||||||
|
|
||||||
// ── Dict / special ──
|
// ── Словари / особые ──
|
||||||
|
|
||||||
public const string ColState = "colState";
|
public const string ColState = "colState";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace Deal.Modules.Tenants.Application.Models;
|
namespace Deal.Modules.Tenants.Application.Models;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения записей аудита
|
||||||
/// Расширения записей аудита
|
|
||||||
/// </summary>
|
|
||||||
internal static class AuditRecordDtoExtensions
|
internal static class AuditRecordDtoExtensions
|
||||||
{
|
{
|
||||||
// События аудита «неудачный вход» (тенант/оператор).
|
// События аудита «неудачный вход» (тенант/оператор).
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public sealed class AuthService(
|
|||||||
string password,
|
string password,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var normalizedLogin = NormalizeLogin(login);
|
string normalizedLogin = NormalizeLogin(login);
|
||||||
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(password))
|
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
return new LoginResultDto(null, null);
|
return new LoginResultDto(null, null);
|
||||||
@@ -61,7 +61,7 @@ public sealed class AuthService(
|
|||||||
Error: LoginResultDto.ErrorTenantSuspended);
|
Error: LoginResultDto.ErrorTenantSuspended);
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await CreateSessionForUserAsync(user.Id, user.Login, impersonatedByOperatorId: null, ct);
|
string token = await CreateSessionForUserAsync(user.Id, user.Login, impersonatedByOperatorId: null, ct);
|
||||||
return new LoginResultDto(user.Login, token, user.Id, user.TenantId);
|
return new LoginResultDto(user.Login, token, user.Id, user.TenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ public sealed class AuthService(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
var session = await authStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
var session = await authStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
||||||
await authStore.DeleteSessionAsync(tokenHash, ct);
|
await authStore.DeleteSessionAsync(tokenHash, ct);
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ public sealed class AuthService(
|
|||||||
string newPassword,
|
string newPassword,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var normalizedLogin = NormalizeLogin(login);
|
string normalizedLogin = NormalizeLogin(login);
|
||||||
var user = await authStore.FindUserByLoginAsync(normalizedLogin, ct);
|
var user = await authStore.FindUserByLoginAsync(normalizedLogin, ct);
|
||||||
if (user is null || oldPassword is null || !passwordHasher.Verify(oldPassword, user.PasswordHash))
|
if (user is null || oldPassword is null || !passwordHasher.Verify(oldPassword, user.PasswordHash))
|
||||||
{
|
{
|
||||||
@@ -125,7 +125,7 @@ public sealed class AuthService(
|
|||||||
|
|
||||||
await authStore.DeleteSessionsByUserIdAsync(user.Id, ct);
|
await authStore.DeleteSessionsByUserIdAsync(user.Id, ct);
|
||||||
await authStore.UpdatePasswordHashAsync(user.Id, passwordHasher.Hash(newPassword), ct);
|
await authStore.UpdatePasswordHashAsync(user.Id, passwordHasher.Hash(newPassword), ct);
|
||||||
var token = await CreateSessionForUserAsync(user.Id, user.Login, impersonatedByOperatorId: null, ct);
|
string token = await CreateSessionForUserAsync(user.Id, user.Login, impersonatedByOperatorId: null, ct);
|
||||||
return new ChangePasswordResultDto(Ok: true, Error: null, NewToken: token);
|
return new ChangePasswordResultDto(Ok: true, Error: null, NewToken: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ public sealed class AuthService(
|
|||||||
return ImpersonationResultDto.Failed(ImpersonationResultDto.ErrorTenantNotFound);
|
return ImpersonationResultDto.Failed(ImpersonationResultDto.ErrorTenantNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
var normalizedLogin = NormalizeLogin(targetLogin);
|
string normalizedLogin = NormalizeLogin(targetLogin);
|
||||||
if (string.IsNullOrEmpty(normalizedLogin))
|
if (string.IsNullOrEmpty(normalizedLogin))
|
||||||
{
|
{
|
||||||
// login не задан — первый пользователь тенанта (по CreatedAt, порядок ListUsersByTenantIdAsync).
|
// login не задан — первый пользователь тенанта (по CreatedAt, порядок ListUsersByTenantIdAsync).
|
||||||
@@ -159,7 +159,7 @@ public sealed class AuthService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
UserIdentityDto firstUser = tenantUsers[0];
|
UserIdentityDto firstUser = tenantUsers[0];
|
||||||
var rawTokenForFirst = await CreateSessionForUserAsync(firstUser.Id, firstUser.Login, operatorId, ct);
|
string rawTokenForFirst = await CreateSessionForUserAsync(firstUser.Id, firstUser.Login, operatorId, ct);
|
||||||
return Success(rawTokenForFirst, firstUser.Id, firstUser.Login, firstUser.TenantId);
|
return Success(rawTokenForFirst, firstUser.Id, firstUser.Login, firstUser.TenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ public sealed class AuthService(
|
|||||||
return ImpersonationResultDto.Failed(ImpersonationResultDto.ErrorUserNotFound);
|
return ImpersonationResultDto.Failed(ImpersonationResultDto.ErrorUserNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawToken = await CreateSessionForUserAsync(user.Id, user.Login, operatorId, ct);
|
string rawToken = await CreateSessionForUserAsync(user.Id, user.Login, operatorId, ct);
|
||||||
return Success(rawToken, user.Id, user.Login, user.TenantId);
|
return Success(rawToken, user.Id, user.Login, user.TenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ public sealed class AuthService(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
var session = await authStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
var session = await authStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
||||||
UserIdentityDto? user = null;
|
UserIdentityDto? user = null;
|
||||||
if (session is not null && session.ExpiresAt > DateTimeOffset.UtcNow)
|
if (session is not null && session.ExpiresAt > DateTimeOffset.UtcNow)
|
||||||
@@ -230,7 +230,7 @@ public sealed class AuthService(
|
|||||||
Guid? impersonatedByOperatorId,
|
Guid? impersonatedByOperatorId,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var rawToken = SessionTokens.NewToken();
|
string rawToken = SessionTokens.NewToken();
|
||||||
var session = new SessionDto(
|
var session = new SessionDto(
|
||||||
TokenHash: SessionTokens.HashToken(rawToken),
|
TokenHash: SessionTokens.HashToken(rawToken),
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public sealed class OperatorAuthService(IOperatorAuthStore operatorAuthStore, IP
|
|||||||
string password,
|
string password,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var normalizedLogin = NormalizeLogin(login);
|
string normalizedLogin = NormalizeLogin(login);
|
||||||
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(password))
|
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
return new OperatorLoginResultDto(null, null);
|
return new OperatorLoginResultDto(null, null);
|
||||||
@@ -39,7 +39,7 @@ public sealed class OperatorAuthService(IOperatorAuthStore operatorAuthStore, IP
|
|||||||
return new OperatorLoginResultDto(null, null);
|
return new OperatorLoginResultDto(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await CreateSessionForOperatorAsync(operatorRecord, ct);
|
string token = await CreateSessionForOperatorAsync(operatorRecord, ct);
|
||||||
return new OperatorLoginResultDto(operatorRecord.Login, token, operatorRecord.Id);
|
return new OperatorLoginResultDto(operatorRecord.Login, token, operatorRecord.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public sealed class OperatorAuthService(IOperatorAuthStore operatorAuthStore, IP
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
var session = await operatorAuthStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
var session = await operatorAuthStore.FindSessionByTokenHashAsync(tokenHash, ct);
|
||||||
OperatorIdentityDto? operatorIdentity = null;
|
OperatorIdentityDto? operatorIdentity = null;
|
||||||
if (session is not null && session.ExpiresAt > DateTimeOffset.UtcNow)
|
if (session is not null && session.ExpiresAt > DateTimeOffset.UtcNow)
|
||||||
@@ -83,7 +83,7 @@ public sealed class OperatorAuthService(IOperatorAuthStore operatorAuthStore, IP
|
|||||||
else if (session is not null)
|
else if (session is not null)
|
||||||
{
|
{
|
||||||
// Очистка протухших сессий — только при обнаружении протухшей (редкий случай), не на каждый запрос
|
// Очистка протухших сессий — только при обнаружении протухшей (редкий случай), не на каждый запрос
|
||||||
// (hot-path, Security review).
|
// (горячий путь запросов).
|
||||||
await operatorAuthStore.DeleteExpiredSessionsAsync(ct);
|
await operatorAuthStore.DeleteExpiredSessionsAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ public sealed class OperatorAuthService(IOperatorAuthStore operatorAuthStore, IP
|
|||||||
// Возвращает: Raw-токен для выдачи клиенту.
|
// Возвращает: Raw-токен для выдачи клиенту.
|
||||||
private async Task<string> CreateSessionForOperatorAsync(StoredOperatorDto operatorRecord, CancellationToken ct)
|
private async Task<string> CreateSessionForOperatorAsync(StoredOperatorDto operatorRecord, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var rawToken = SessionTokens.NewToken();
|
string rawToken = SessionTokens.NewToken();
|
||||||
var session = new OperatorSessionDto(
|
var session = new OperatorSessionDto(
|
||||||
TokenHash: SessionTokens.HashToken(rawToken),
|
TokenHash: SessionTokens.HashToken(rawToken),
|
||||||
OperatorId: operatorRecord.Id,
|
OperatorId: operatorRecord.Id,
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ public sealed class OperatorBootstrapService(IOperatorAuthStore operatorAuthStor
|
|||||||
bool allowDevelopmentDefaults,
|
bool allowDevelopmentDefaults,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var normalizedLogin = NormalizeLogin(login);
|
string normalizedLogin = NormalizeLogin(login);
|
||||||
var resolvedPassword = password;
|
string? resolvedPassword = password;
|
||||||
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(resolvedPassword))
|
if (string.IsNullOrEmpty(normalizedLogin) || string.IsNullOrEmpty(resolvedPassword))
|
||||||
{
|
{
|
||||||
if (!allowDevelopmentDefaults)
|
if (!allowDevelopmentDefaults)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public static class SessionTokens
|
|||||||
/// <returns>64 hex-символа.</returns>
|
/// <returns>64 hex-символа.</returns>
|
||||||
public static string HashToken(string rawToken)
|
public static string HashToken(string rawToken)
|
||||||
{
|
{
|
||||||
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(rawToken));
|
byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(rawToken));
|
||||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public sealed class TenantService(ITenantRepository tenantRepository, ITenantPro
|
|||||||
CreatedAt: DateTimeOffset.UtcNow),
|
CreatedAt: DateTimeOffset.UtcNow),
|
||||||
ct);
|
ct);
|
||||||
|
|
||||||
var tenantIdValue = id.ToString("N");
|
string tenantIdValue = id.ToString("N");
|
||||||
await tenantProvisioner.ProvisionAsync(new TenantId(tenantIdValue), ct);
|
await tenantProvisioner.ProvisionAsync(new TenantId(tenantIdValue), ct);
|
||||||
return new TenantId(tenantIdValue);
|
return new TenantId(tenantIdValue);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ namespace Deal.SharedKernel.Tenants.Abstractions;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITenantContext
|
public interface ITenantContext
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Идентификатор текущего тенанта или null для системного контекста
|
||||||
|
/// </summary>
|
||||||
public TenantId? TenantId { get; }
|
public TenantId? TenantId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Признак наличия текущего тенанта
|
||||||
|
/// </summary>
|
||||||
public bool HasTenant { get; }
|
public bool HasTenant { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public static class UrlSafeToken
|
|||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(byteCount);
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(byteCount);
|
||||||
|
|
||||||
var bytes = new byte[byteCount];
|
byte[] bytes = new byte[byteCount];
|
||||||
RandomNumberGenerator.Fill(bytes);
|
RandomNumberGenerator.Fill(bytes);
|
||||||
return Convert.ToBase64String(bytes)
|
return Convert.ToBase64String(bytes)
|
||||||
.TrimEnd('=')
|
.TrimEnd('=')
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public sealed class AuthStoreTests
|
|||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
await CreateUserAsync(store, Guid.NewGuid(), "user@example.com");
|
await CreateUserAsync(store, Guid.NewGuid(), "user@example.com");
|
||||||
var operatorId = Guid.NewGuid();
|
var operatorId = Guid.NewGuid();
|
||||||
var tokenHash = "token-hash-impersonation";
|
string tokenHash = "token-hash-impersonation";
|
||||||
|
|
||||||
await store.CreateSessionAsync(
|
await store.CreateSessionAsync(
|
||||||
new SessionDto(tokenHash, userId, "user@example.com", DateTimeOffset.UtcNow.AddDays(30), operatorId),
|
new SessionDto(tokenHash, userId, "user@example.com", DateTimeOffset.UtcNow.AddDays(30), operatorId),
|
||||||
@@ -49,7 +49,7 @@ public sealed class AuthStoreTests
|
|||||||
Assert.Equal(userId, found.UserId);
|
Assert.Equal(userId, found.UserId);
|
||||||
|
|
||||||
// Обычная сессия (без маркера) читается как ImpersonatedByOperatorId=null.
|
// Обычная сессия (без маркера) читается как ImpersonatedByOperatorId=null.
|
||||||
var plainHash = "token-hash-plain";
|
string plainHash = "token-hash-plain";
|
||||||
await store.CreateSessionAsync(
|
await store.CreateSessionAsync(
|
||||||
new SessionDto(plainHash, userId, "user@example.com", DateTimeOffset.UtcNow.AddDays(30)),
|
new SessionDto(plainHash, userId, "user@example.com", DateTimeOffset.UtcNow.AddDays(30)),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public sealed class TenantSchemaMigrationServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task MigrateAllAsync_OneSchemaFails_ContinuesAndReportsFailed()
|
public async Task MigrateAllAsync_OneSchemaFails_ContinuesAndReportsFailed()
|
||||||
{
|
{
|
||||||
var failingSchema = $"tenant_{TenantA:N}";
|
string failingSchema = $"tenant_{TenantA:N}";
|
||||||
var provisioner = new FailingTenantProvisioner(failingSchema);
|
var provisioner = new FailingTenantProvisioner(failingSchema);
|
||||||
var repository = new FakeTenantRepository(
|
var repository = new FakeTenantRepository(
|
||||||
TenantRecord(TenantA, "A"),
|
TenantRecord(TenantA, "A"),
|
||||||
@@ -115,7 +115,7 @@ public sealed class TenantSchemaMigrationServiceTests
|
|||||||
public async Task MigrateAllAsync_SchemaFailsWithinPage_ContinuesOthers()
|
public async Task MigrateAllAsync_SchemaFailsWithinPage_ContinuesOthers()
|
||||||
{
|
{
|
||||||
TenantRecordDto[] records = Records(3);
|
TenantRecordDto[] records = Records(3);
|
||||||
var failingSchema = $"tenant_{records[0].Id:N}";
|
string failingSchema = $"tenant_{records[0].Id:N}";
|
||||||
var provisioner = new FailingTenantProvisioner(failingSchema);
|
var provisioner = new FailingTenantProvisioner(failingSchema);
|
||||||
var repository = new FakeTenantRepository(records);
|
var repository = new FakeTenantRepository(records);
|
||||||
var service = NewService(repository, provisioner);
|
var service = NewService(repository, provisioner);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public sealed class TenantSchemaMigratorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateSchemaSql_IsEscaped()
|
public void CreateSchemaSql_IsEscaped()
|
||||||
{
|
{
|
||||||
var sql = TenantSchemaMigrator.CreateSchemaSql("tenant_abc");
|
string sql = TenantSchemaMigrator.CreateSchemaSql("tenant_abc");
|
||||||
Assert.Contains("CREATE SCHEMA IF NOT EXISTS \"tenant_abc\"", sql);
|
Assert.Contains("CREATE SCHEMA IF NOT EXISTS \"tenant_abc\"", sql);
|
||||||
Assert.DoesNotContain("; DROP", sql);
|
Assert.DoesNotContain("; DROP", sql);
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ public sealed class TenantSchemaMigratorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateSchemaSql_EscapesQuotes()
|
public void CreateSchemaSql_EscapesQuotes()
|
||||||
{
|
{
|
||||||
var sql = TenantSchemaMigrator.CreateSchemaSql("tenant_a\"b");
|
string sql = TenantSchemaMigrator.CreateSchemaSql("tenant_a\"b");
|
||||||
Assert.Contains("\"tenant_a\"\"b\"", sql);
|
Assert.Contains("\"tenant_a\"\"b\"", sql);
|
||||||
Assert.DoesNotContain("\"tenant_a\"b\"", sql);
|
Assert.DoesNotContain("\"tenant_a\"b\"", sql);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public sealed class PromptDefaultsTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Fill_MoreThanSixtyKeywords_TakesFirstSixty()
|
public void Fill_MoreThanSixtyKeywords_TakesFirstSixty()
|
||||||
{
|
{
|
||||||
var keywords = Enumerable.Range(1, 65).Select(index => $"слово-{index}").ToArray();
|
string[] keywords = Enumerable.Range(1, 65).Select(index => $"слово-{index}").ToArray();
|
||||||
|
|
||||||
string result = PromptFiller.Fill("{keywords}", "сфера", keywords);
|
string result = PromptFiller.Fill("{keywords}", "сфера", keywords);
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public sealed class AuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WithExpiredSession_ReturnsNull()
|
public async Task ResolveSessionAsync_WithExpiredSession_ReturnsNull()
|
||||||
{
|
{
|
||||||
var rawToken = "expired-session-raw-token";
|
string rawToken = "expired-session-raw-token";
|
||||||
_store.AddSession(new SessionDto(
|
_store.AddSession(new SessionDto(
|
||||||
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(-1)));
|
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(-1)));
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ public sealed class AuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WithValidSession_ReturnsUser()
|
public async Task ResolveSessionAsync_WithValidSession_ReturnsUser()
|
||||||
{
|
{
|
||||||
var rawToken = "valid-session-raw-token";
|
string rawToken = "valid-session-raw-token";
|
||||||
_store.AddSession(new SessionDto(
|
_store.AddSession(new SessionDto(
|
||||||
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ public sealed class AuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WhenTenantSuspended_ReturnsNullImmediately()
|
public async Task ResolveSessionAsync_WhenTenantSuspended_ReturnsNullImmediately()
|
||||||
{
|
{
|
||||||
var rawToken = "suspended-tenant-session";
|
string rawToken = "suspended-tenant-session";
|
||||||
_store.AddSession(new SessionDto(
|
_store.AddSession(new SessionDto(
|
||||||
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
SessionTokens.HashToken(rawToken), UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
||||||
await _tenantStore.UpdateStatusAsync(UserTenantId, TenantStatuses.Suspended, CancellationToken.None);
|
await _tenantStore.UpdateStatusAsync(UserTenantId, TenantStatuses.Suspended, CancellationToken.None);
|
||||||
@@ -181,8 +181,8 @@ public sealed class AuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LogoutAsync_WithToken_DeletesSession()
|
public async Task LogoutAsync_WithToken_DeletesSession()
|
||||||
{
|
{
|
||||||
var rawToken = "logout-session-raw-token";
|
string rawToken = "logout-session-raw-token";
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
||||||
|
|
||||||
await _service.LogoutAsync(rawToken, CancellationToken.None);
|
await _service.LogoutAsync(rawToken, CancellationToken.None);
|
||||||
@@ -330,8 +330,8 @@ public sealed class AuthServiceTests
|
|||||||
public async Task LogoutAsync_ForImpersonationSession_ReturnsStopInfoAndDeletesSession()
|
public async Task LogoutAsync_ForImpersonationSession_ReturnsStopInfoAndDeletesSession()
|
||||||
{
|
{
|
||||||
var operatorId = Guid.NewGuid();
|
var operatorId = Guid.NewGuid();
|
||||||
var rawToken = "impersonation-session-raw-token";
|
string rawToken = "impersonation-session-raw-token";
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30), operatorId));
|
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30), operatorId));
|
||||||
|
|
||||||
var logout = await _service.LogoutAsync(rawToken, CancellationToken.None);
|
var logout = await _service.LogoutAsync(rawToken, CancellationToken.None);
|
||||||
@@ -347,8 +347,8 @@ public sealed class AuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LogoutAsync_ForNormalSession_ReturnsNull()
|
public async Task LogoutAsync_ForNormalSession_ReturnsNull()
|
||||||
{
|
{
|
||||||
var rawToken = "normal-session-raw-token";
|
string rawToken = "normal-session-raw-token";
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
_store.AddSession(new SessionDto(tokenHash, UserId, UserLogin, DateTimeOffset.UtcNow.AddDays(30)));
|
||||||
|
|
||||||
var logout = await _service.LogoutAsync(rawToken, CancellationToken.None);
|
var logout = await _service.LogoutAsync(rawToken, CancellationToken.None);
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public sealed class FakeAuthStore : IAuthStore
|
|||||||
string passwordHash,
|
string passwordHash,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var index = _users.FindIndex(u => u.Id == userId);
|
int index = _users.FindIndex(u => u.Id == userId);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
var user = _users[index];
|
var user = _users[index];
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public sealed class OperatorAuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WithValidSession_ReturnsOperatorIdentity()
|
public async Task ResolveSessionAsync_WithValidSession_ReturnsOperatorIdentity()
|
||||||
{
|
{
|
||||||
var rawToken = "valid-operator-session-raw-token";
|
string rawToken = "valid-operator-session-raw-token";
|
||||||
_store.AddSession(new OperatorSessionDto(
|
_store.AddSession(new OperatorSessionDto(
|
||||||
SessionTokens.HashToken(rawToken), OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
SessionTokens.HashToken(rawToken), OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ public sealed class OperatorAuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WithExpiredSession_ReturnsNull()
|
public async Task ResolveSessionAsync_WithExpiredSession_ReturnsNull()
|
||||||
{
|
{
|
||||||
var rawToken = "expired-operator-session-raw-token";
|
string rawToken = "expired-operator-session-raw-token";
|
||||||
_store.AddSession(new OperatorSessionDto(
|
_store.AddSession(new OperatorSessionDto(
|
||||||
SessionTokens.HashToken(rawToken), OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(-1)));
|
SessionTokens.HashToken(rawToken), OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(-1)));
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ public sealed class OperatorAuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResolveSessionAsync_WhenOperatorWasDeleted_ReturnsNull()
|
public async Task ResolveSessionAsync_WhenOperatorWasDeleted_ReturnsNull()
|
||||||
{
|
{
|
||||||
var rawToken = "orphan-operator-session-raw-token";
|
string rawToken = "orphan-operator-session-raw-token";
|
||||||
_store.AddSession(new OperatorSessionDto(
|
_store.AddSession(new OperatorSessionDto(
|
||||||
SessionTokens.HashToken(rawToken), Guid.NewGuid(), "deleted-operator", DateTimeOffset.UtcNow.AddHours(12)));
|
SessionTokens.HashToken(rawToken), Guid.NewGuid(), "deleted-operator", DateTimeOffset.UtcNow.AddHours(12)));
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ public sealed class OperatorAuthServiceTests
|
|||||||
const string suspendedLogin = "suspended-operator";
|
const string suspendedLogin = "suspended-operator";
|
||||||
var passwordHasher = new FakePasswordHasher();
|
var passwordHasher = new FakePasswordHasher();
|
||||||
_store.AddOperator(new StoredOperatorDto(Guid.NewGuid(), suspendedLogin, "suspended", passwordHasher.Hash("x")));
|
_store.AddOperator(new StoredOperatorDto(Guid.NewGuid(), suspendedLogin, "suspended", passwordHasher.Hash("x")));
|
||||||
var rawToken = "suspended-operator-session-raw-token";
|
string rawToken = "suspended-operator-session-raw-token";
|
||||||
_store.AddSession(new OperatorSessionDto(
|
_store.AddSession(new OperatorSessionDto(
|
||||||
SessionTokens.HashToken(rawToken), Guid.NewGuid(), suspendedLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
SessionTokens.HashToken(rawToken), Guid.NewGuid(), suspendedLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
||||||
|
|
||||||
@@ -141,8 +141,8 @@ public sealed class OperatorAuthServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LogoutAsync_WithToken_DeletesSession()
|
public async Task LogoutAsync_WithToken_DeletesSession()
|
||||||
{
|
{
|
||||||
var rawToken = "logout-operator-session-raw-token";
|
string rawToken = "logout-operator-session-raw-token";
|
||||||
var tokenHash = SessionTokens.HashToken(rawToken);
|
string tokenHash = SessionTokens.HashToken(rawToken);
|
||||||
_store.AddSession(new OperatorSessionDto(tokenHash, OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
_store.AddSession(new OperatorSessionDto(tokenHash, OperatorId, OperatorLogin, DateTimeOffset.UtcNow.AddHours(12)));
|
||||||
|
|
||||||
await _service.LogoutAsync(rawToken, CancellationToken.None);
|
await _service.LogoutAsync(rawToken, CancellationToken.None);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task InDevelopment_WithoutEnvCredentials_SeedsDefaultOperator()
|
public async Task InDevelopment_WithoutEnvCredentials_SeedsDefaultOperator()
|
||||||
{
|
{
|
||||||
var login = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
string? login = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, login);
|
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, login);
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task InDevelopment_WithEmptyEnvCredentials_SeedsDefaultOperator()
|
public async Task InDevelopment_WithEmptyEnvCredentials_SeedsDefaultOperator()
|
||||||
{
|
{
|
||||||
var login = await _service.EnsureOperatorAsync("", "", allowDevelopmentDefaults: true, CancellationToken.None);
|
string? login = await _service.EnsureOperatorAsync("", "", allowDevelopmentDefaults: true, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, login);
|
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, login);
|
||||||
Assert.Single(_store.Operators);
|
Assert.Single(_store.Operators);
|
||||||
@@ -45,8 +45,8 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task RepeatedBootstrap_IsIdempotent_KeepsSingleOperator()
|
public async Task RepeatedBootstrap_IsIdempotent_KeepsSingleOperator()
|
||||||
{
|
{
|
||||||
var first = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
string? first = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
||||||
var second = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
string? second = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: true, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, first);
|
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, first);
|
||||||
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, second);
|
Assert.Equal(OperatorBootstrapService.DefaultOperatorLogin, second);
|
||||||
@@ -58,7 +58,7 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task Production_WithoutEnvCredentials_SkipsBootstrap()
|
public async Task Production_WithoutEnvCredentials_SkipsBootstrap()
|
||||||
{
|
{
|
||||||
var login = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: false, CancellationToken.None);
|
string? login = await _service.EnsureOperatorAsync(null, null, allowDevelopmentDefaults: false, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Null(login);
|
Assert.Null(login);
|
||||||
Assert.Empty(_store.Operators);
|
Assert.Empty(_store.Operators);
|
||||||
@@ -68,7 +68,7 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task Production_WithEnvCredentials_CreatesOperatorWithNormalizedLogin()
|
public async Task Production_WithEnvCredentials_CreatesOperatorWithNormalizedLogin()
|
||||||
{
|
{
|
||||||
var login = await _service.EnsureOperatorAsync(
|
string? login = await _service.EnsureOperatorAsync(
|
||||||
" Root-Admin ", "root-password", allowDevelopmentDefaults: false, CancellationToken.None);
|
" Root-Admin ", "root-password", allowDevelopmentDefaults: false, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal("root-admin", login);
|
Assert.Equal("root-admin", login);
|
||||||
@@ -83,7 +83,7 @@ public sealed class OperatorBootstrapServiceTests
|
|||||||
{
|
{
|
||||||
_store.AddOperator(new StoredOperatorDto(Guid.NewGuid(), "existing-op", "active", _passwordHasher.Hash("old-password")));
|
_store.AddOperator(new StoredOperatorDto(Guid.NewGuid(), "existing-op", "active", _passwordHasher.Hash("old-password")));
|
||||||
|
|
||||||
var login = await _service.EnsureOperatorAsync("EXISTING-OP", "new-password", allowDevelopmentDefaults: false, CancellationToken.None);
|
string? login = await _service.EnsureOperatorAsync("EXISTING-OP", "new-password", allowDevelopmentDefaults: false, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal("existing-op", login);
|
Assert.Equal("existing-op", login);
|
||||||
Assert.Single(_store.Operators);
|
Assert.Single(_store.Operators);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public sealed class PasswordHasherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Hash_ReturnsArgon2idEncodedStringDifferentFromPassword()
|
public void Hash_ReturnsArgon2idEncodedStringDifferentFromPassword()
|
||||||
{
|
{
|
||||||
var hash = _hasher.Hash("correct horse battery staple");
|
string hash = _hasher.Hash("correct horse battery staple");
|
||||||
|
|
||||||
Assert.False(string.IsNullOrWhiteSpace(hash));
|
Assert.False(string.IsNullOrWhiteSpace(hash));
|
||||||
Assert.StartsWith("$argon2id$", hash);
|
Assert.StartsWith("$argon2id$", hash);
|
||||||
@@ -23,7 +23,7 @@ public sealed class PasswordHasherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Verify_WithCorrectPassword_ReturnsTrue()
|
public void Verify_WithCorrectPassword_ReturnsTrue()
|
||||||
{
|
{
|
||||||
var hash = _hasher.Hash("secret42");
|
string hash = _hasher.Hash("secret42");
|
||||||
|
|
||||||
Assert.True(_hasher.Verify("secret42", hash));
|
Assert.True(_hasher.Verify("secret42", hash));
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public sealed class PasswordHasherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Verify_WithWrongPassword_ReturnsFalse()
|
public void Verify_WithWrongPassword_ReturnsFalse()
|
||||||
{
|
{
|
||||||
var hash = _hasher.Hash("secret42");
|
string hash = _hasher.Hash("secret42");
|
||||||
|
|
||||||
Assert.False(_hasher.Verify("wrong-password", hash));
|
Assert.False(_hasher.Verify("wrong-password", hash));
|
||||||
}
|
}
|
||||||
@@ -39,8 +39,8 @@ public sealed class PasswordHasherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Hash_SamePasswordTwice_DifferentHashesBecauseOfRandomSalt()
|
public void Hash_SamePasswordTwice_DifferentHashesBecauseOfRandomSalt()
|
||||||
{
|
{
|
||||||
var first = _hasher.Hash("same-password");
|
string first = _hasher.Hash("same-password");
|
||||||
var second = _hasher.Hash("same-password");
|
string second = _hasher.Hash("same-password");
|
||||||
|
|
||||||
Assert.NotEqual(first, second);
|
Assert.NotEqual(first, second);
|
||||||
Assert.True(_hasher.Verify("same-password", first));
|
Assert.True(_hasher.Verify("same-password", first));
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ public sealed class SessionTokensTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void NewToken_ReturnsUniqueLongEnoughTokens()
|
public void NewToken_ReturnsUniqueLongEnoughTokens()
|
||||||
{
|
{
|
||||||
var first = SessionTokens.NewToken();
|
string first = SessionTokens.NewToken();
|
||||||
var second = SessionTokens.NewToken();
|
string second = SessionTokens.NewToken();
|
||||||
|
|
||||||
Assert.NotEqual(first, second);
|
Assert.NotEqual(first, second);
|
||||||
Assert.True(first.Length > 32);
|
Assert.True(first.Length > 32);
|
||||||
@@ -21,7 +21,7 @@ public sealed class SessionTokensTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void NewToken_UsesBase64UrlAlphabetWithoutPadding()
|
public void NewToken_UsesBase64UrlAlphabetWithoutPadding()
|
||||||
{
|
{
|
||||||
var token = SessionTokens.NewToken();
|
string token = SessionTokens.NewToken();
|
||||||
|
|
||||||
// 32 байта в Base64Url без padding: 43 символа, без '=' и без небезопасных '+'/ '/'.
|
// 32 байта в Base64Url без padding: 43 символа, без '=' и без небезопасных '+'/ '/'.
|
||||||
Assert.Equal(43, token.Length);
|
Assert.Equal(43, token.Length);
|
||||||
@@ -33,8 +33,8 @@ public sealed class SessionTokensTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void HashToken_IsDeterministicHexSha256()
|
public void HashToken_IsDeterministicHexSha256()
|
||||||
{
|
{
|
||||||
var first = SessionTokens.HashToken("raw-token-value");
|
string first = SessionTokens.HashToken("raw-token-value");
|
||||||
var second = SessionTokens.HashToken("raw-token-value");
|
string second = SessionTokens.HashToken("raw-token-value");
|
||||||
|
|
||||||
Assert.Equal(first, second);
|
Assert.Equal(first, second);
|
||||||
Assert.Equal(64, first.Length);
|
Assert.Equal(64, first.Length);
|
||||||
@@ -43,8 +43,8 @@ public sealed class SessionTokensTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void HashToken_DifferentTokensProduceDifferentHashes()
|
public void HashToken_DifferentTokensProduceDifferentHashes()
|
||||||
{
|
{
|
||||||
var first = SessionTokens.HashToken("raw-token-1");
|
string first = SessionTokens.HashToken("raw-token-1");
|
||||||
var second = SessionTokens.HashToken("raw-token-2");
|
string second = SessionTokens.HashToken("raw-token-2");
|
||||||
|
|
||||||
Assert.NotEqual(first, second);
|
Assert.NotEqual(first, second);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public sealed class ConversionRecomputerTests
|
|||||||
|
|
||||||
int updated = await service.RecomputeAsync(CancellationToken.None);
|
int updated = await service.RecomputeAsync(CancellationToken.None);
|
||||||
|
|
||||||
// 100 USD → EUR: 100 * 92.5 / 99.9 = 92.5925… → round 2.
|
// 100 USD → EUR: 100 * 92.5 / 99.9 = 92.5925… → округление до 2 знаков.
|
||||||
Assert.Equal(1, updated);
|
Assert.Equal(1, updated);
|
||||||
AssertConverted(store, "l_board", convFrom: 92.59, convTo: 92.59, "EUR");
|
AssertConverted(store, "l_board", convFrom: 92.59, convTo: 92.59, "EUR");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Deal.Grpc.Hosting.Models;
|
using Deal.Grpc.Hosting.Models;
|
||||||
using Deal.Grpc.Hosting.Options;
|
using Deal.Grpc.Hosting.Options;
|
||||||
using Deal.Grpc.Hosting.Services;
|
using Deal.Grpc.Hosting.Services;
|
||||||
|
using Grpc.Core;
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Interceptors;
|
namespace Deal.Grpc.Hosting.Interceptors;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Deal.Grpc.Hosting.Models;
|
using Deal.Grpc.Hosting.Models;
|
||||||
using Deal.Grpc.Hosting.Options;
|
using Deal.Grpc.Hosting.Options;
|
||||||
using Deal.Grpc.Hosting.Services;
|
using Deal.Grpc.Hosting.Services;
|
||||||
|
using Grpc.Core;
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Interceptors;
|
namespace Deal.Grpc.Hosting.Interceptors;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Deal.Grpc.Hosting.Interceptors;
|
using Deal.Grpc.Hosting.Interceptors;
|
||||||
using Deal.Grpc.Hosting.Models;
|
using Deal.Grpc.Hosting.Models;
|
||||||
using Deal.Grpc.Hosting.Services;
|
using Deal.Grpc.Hosting.Services;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Options;
|
namespace Deal.Grpc.Hosting.Options;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
using Deal.Grpc.Hosting.Interceptors;
|
||||||
|
using Deal.Grpc.Hosting.Models;
|
||||||
|
using Deal.Grpc.Hosting.Options;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using Serilog.Formatting.Compact;
|
using Serilog.Formatting.Compact;
|
||||||
using Deal.Grpc.Hosting.Interceptors;
|
|
||||||
using Deal.Grpc.Hosting.Models;
|
|
||||||
using Deal.Grpc.Hosting.Options;
|
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Services;
|
namespace Deal.Grpc.Hosting.Services;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
using Deal.Grpc.Hosting.Interceptors;
|
||||||
|
using Deal.Grpc.Hosting.Models;
|
||||||
|
using Deal.Grpc.Hosting.Options;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using OpenTelemetry.Metrics;
|
using OpenTelemetry.Metrics;
|
||||||
using Deal.Grpc.Hosting.Interceptors;
|
|
||||||
using Deal.Grpc.Hosting.Models;
|
|
||||||
using Deal.Grpc.Hosting.Options;
|
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Services;
|
namespace Deal.Grpc.Hosting.Services;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Deal.Grpc.Hosting.Interceptors;
|
||||||
|
using Deal.Grpc.Hosting.Models;
|
||||||
|
using Deal.Grpc.Hosting.Options;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using Deal.Grpc.Hosting.Interceptors;
|
|
||||||
using Deal.Grpc.Hosting.Models;
|
|
||||||
using Deal.Grpc.Hosting.Options;
|
|
||||||
|
|
||||||
namespace Deal.Grpc.Hosting.Services;
|
namespace Deal.Grpc.Hosting.Services;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using Deal.Ml.Model;
|
|||||||
|
|
||||||
namespace Deal.Ml.Extensions;
|
namespace Deal.Ml.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения string для меток классов ML-модели.
|
||||||
/// Расширения <see cref="string"/> для меток классов ML-модели.
|
|
||||||
/// </summary>
|
|
||||||
internal static class LabelExtensions
|
internal static class LabelExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public sealed class DialogHueTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Compute_DifferentSources_DistributeAcrossPalette()
|
public void Compute_DifferentSources_DistributeAcrossPalette()
|
||||||
{
|
{
|
||||||
var hues = new[]
|
string[] hues = new[]
|
||||||
{
|
{
|
||||||
DialogHue.Compute("-100111", "Канал А"),
|
DialogHue.Compute("-100111", "Канал А"),
|
||||||
DialogHue.Compute("-100222", "Канал Б"),
|
DialogHue.Compute("-100222", "Канал Б"),
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using Grpc.Core;
|
|||||||
|
|
||||||
namespace Deal.Telegram.Extensions;
|
namespace Deal.Telegram.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
// Расширения Exception для классификации сбоев telegram-service.
|
||||||
/// Расширения <see cref="Exception"/> для классификации сбоев telegram-service.
|
|
||||||
/// </summary>
|
|
||||||
internal static class ExceptionExtensions
|
internal static class ExceptionExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user