diff --git a/.claude/project-context.md b/.claude/project-context.md index 97b464b..717f527 100644 --- a/.claude/project-context.md +++ b/.claude/project-context.md @@ -5,6 +5,15 @@ ## RECENT CHANGES (This Session) +**Seq Removal - Logging Cleanup (Dec 5, 2025):** +- ✅ Removed Seq logging sink to eliminate commercial licensing concerns +- ✅ Removed `Serilog.Sinks.Seq` NuGet package from DiunaBI.API.csproj +- ✅ Removed Seq sink configuration from appsettings.Development.json +- ✅ Kept Serilog (free, open-source) with Console + File sinks for production-ready logging +- ✅ Build verified - no errors after Seq removal +- Files modified: [DiunaBI.API.csproj](DiunaBI.API/DiunaBI.API.csproj), [appsettings.Development.json](DiunaBI.API/appsettings.Development.json) +- Manual step required: Remove `seq` service from docker-compose.yml and add Docker log rotation config + **UI Reorganization (Dec 5, 2025):** - ✅ Moved pages to feature-based folders: `Pages/Layers/`, `Pages/Jobs/`, `Pages/DataInbox/` - ✅ Organized components: `Components/Layout/` (MainLayout, EmptyLayout, Routes), `Components/Auth/` (AuthGuard, LoginCard) @@ -27,7 +36,7 @@ - UI: MudBlazor 8.0 - Real-time: SignalR (EntityChangeHub) - Google: Sheets API, Drive API, OAuth -- Logging: Serilog (Console, File, Seq) +- Logging: Serilog (Console, File) - Auth: JWT Bearer + Google OAuth --- diff --git a/DiunaBI.API/DiunaBI.API.csproj b/DiunaBI.API/DiunaBI.API.csproj index e464ea4..0e609e3 100644 --- a/DiunaBI.API/DiunaBI.API.csproj +++ b/DiunaBI.API/DiunaBI.API.csproj @@ -20,7 +20,6 @@ - diff --git a/DiunaBI.API/Program.cs b/DiunaBI.API/Program.cs index 0e08ff8..c530385 100644 --- a/DiunaBI.API/Program.cs +++ b/DiunaBI.API/Program.cs @@ -246,10 +246,6 @@ app.Use(async (context, next) => logger.LogError(ex, "❌ Failed to extract UserId from JWT token"); } } - else - { - logger.LogWarning("❌ No valid Bearer token found"); - } await next(context); }); diff --git a/DiunaBI.UI.Shared/Components/Auth/LoginCard.razor b/DiunaBI.UI.Shared/Components/Auth/LoginCard.razor index 6f8b223..5f085f7 100644 --- a/DiunaBI.UI.Shared/Components/Auth/LoginCard.razor +++ b/DiunaBI.UI.Shared/Components/Auth/LoginCard.razor @@ -37,15 +37,36 @@ @_errorMessage } + + @if (_sessionExpired) + { + + Your session has expired. Please sign in again. + + } @code { private bool _isLoading = false; private string _errorMessage = string.Empty; + private bool _sessionExpired = false; private static LoginCard? _instance; private bool _isInitialized = false; + protected override void OnInitialized() + { + // Check if sessionExpired query parameter is present + var uri = new Uri(NavigationManager.Uri); + var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + _sessionExpired = query["sessionExpired"] == "true"; + + if (_sessionExpired) + { + Console.WriteLine("⚠️ Session expired - user redirected to login"); + } + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) diff --git a/DiunaBI.UI.Shared/Extensions/ServiceCollectionExtensions.cs b/DiunaBI.UI.Shared/Extensions/ServiceCollectionExtensions.cs index 9ecb8a1..6eb3624 100644 --- a/DiunaBI.UI.Shared/Extensions/ServiceCollectionExtensions.cs +++ b/DiunaBI.UI.Shared/Extensions/ServiceCollectionExtensions.cs @@ -17,14 +17,16 @@ public static class ServiceCollectionExtensions Console.WriteLine($"🔧 Configuring HttpClient with BaseAddress: {baseUri}"); services.AddTransient(); + services.AddTransient(); - // Configure named HttpClient with logging handler + // Configure named HttpClient with logging and 401 handling // Note: Authentication is handled by AuthService setting DefaultRequestHeaders.Authorization services.AddHttpClient("DiunaBI", client => { client.BaseAddress = new Uri(baseUri); Console.WriteLine($"✅ HttpClient BaseAddress set to: {client.BaseAddress}"); }) + .AddHttpMessageHandler() .AddHttpMessageHandler(); // Register a scoped HttpClient factory that services will use diff --git a/DiunaBI.UI.Shared/Handlers/UnauthorizedResponseHandler.cs b/DiunaBI.UI.Shared/Handlers/UnauthorizedResponseHandler.cs new file mode 100644 index 0000000..563586d --- /dev/null +++ b/DiunaBI.UI.Shared/Handlers/UnauthorizedResponseHandler.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using DiunaBI.UI.Shared.Services; + +namespace DiunaBI.UI.Shared.Handlers; + +public class UnauthorizedResponseHandler : DelegatingHandler +{ + private readonly IServiceProvider _serviceProvider; + + public UnauthorizedResponseHandler(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + var response = await base.SendAsync(request, cancellationToken); + + // Check if response is 401 Unauthorized + if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + Console.WriteLine("⚠️ 401 Unauthorized response detected - clearing credentials and redirecting to login"); + + // Create a scope to get scoped services + using var scope = _serviceProvider.CreateScope(); + var authService = scope.ServiceProvider.GetRequiredService(); + var navigationManager = scope.ServiceProvider.GetRequiredService(); + + // Clear authentication + await authService.ClearAuthenticationAsync(); + + // Navigate to login page with session expired message + navigationManager.NavigateTo("/login?sessionExpired=true", forceLoad: true); + } + + return response; + } +}