using Deal.Api.Extensions; using Deal.Api.Models; using Deal.Modules.Tenants.Application.Services; using Deal.SharedKernel.Tenants.Abstractions; using Deal.SharedKernel.Tenants.Models; using Microsoft.Extensions.Options; // Имя конфигурационного типа совпадает с Microsoft.AspNetCore.Http.CookieOptions — фиксируем алиасом. using CookieOptions = Deal.Api.Configuration.CookieOptions; namespace Deal.Api.Middleware; /// /// Middleware сессии /// public sealed class SessionMiddleware { private readonly RequestDelegate _next; private readonly IOptionsMonitor _cookieOptions; private readonly ITenantContext _tenantContext; public SessionMiddleware( RequestDelegate next, IOptionsMonitor cookieOptions, ITenantContext tenantContext) { _next = next; _cookieOptions = cookieOptions; _tenantContext = tenantContext; } /// /// Обрабатывает запрос /// public async Task InvokeAsync(HttpContext context) { try { string cookieName = _cookieOptions.CurrentValue.Name; if (context.Request.Cookies.TryGetValue(cookieName, out string? rawToken) && !string.IsNullOrWhiteSpace(rawToken)) { // AuthService scoped: создаём scope на запрос через RequestServices. await using var scope = context.RequestServices.CreateAsyncScope(); var authService = scope.ServiceProvider.GetRequiredService(); var user = await authService.ResolveSessionAsync(rawToken, context.RequestAborted); if (user is not null) { context.SetCurrentUser(new CurrentUser(user.Id, user.Login, user.TenantId, user.Status)); // Схема тенанта именуется tenant_<id>, где id — Guid в формате "N" (см. TenantService). _tenantContext.SetTenant(new TenantId(user.TenantId.ToString("N"))); } } await _next(context); } finally { // Контекст AsyncLocal не должен переживать запрос. _tenantContext.Reset(); } } }