711480f8f6
This pull request introduces the dedicated containerized infrastructure and configuration for deploying NexusReader's beta version in the Test environment. ### Summary of Changes 1. **Docker Infrastructure & Secrets**: - **`docker-compose.test.yml`**: Configured dedicated database and auxiliary services (PostgreSQL 17, Qdrant, Neo4j) on isolated, non-standard ports to ensure zero conflict with the existing server configurations. - **`.env.test.template`**: Provided an environment variable template showing required setups, including mandatory database passwords, API keys, and admin custom passwords. - **`.gitignore`**: Excluded local `.env` files to prevent accidental commits of production or staging secrets. 2. **Database Hardening**: - Configured Neo4j with basic authentication (`IDriver` instantiation uses basic auth when credentials are provided in configuration). - Configured PostgreSQL to use mandatory authentication. - Configured the admin seeder (`DbInitializer.cs`) to dynamically use `NEXUS_ADMIN_PASSWORD` from environment variables, falling back to a default password in local Development only. 3. **Feature-Flagged Restrictions**: - **`appsettings.Test.json`**: Implemented `Features:AllowRegistration` and `Features:AllowPasswordReset` flags set to `false`. - **Middleware Enforcement (`Program.cs`)**: Intercepts requests to `/identity/register` and `/identity/forgotPassword` (and their MVC/form variations) and rejects them with a `403 Forbidden` response in restricted environments. - **OAuth Provisioning Guard (`Program.cs`)**: Blocks new account provisioning via Google OAuth callback by checking the `Features:AllowRegistration` configuration, redirecting users to the login page with a descriptive error. - **UI Protection (`Login.razor`, `Register.razor`)**: Conditionally hides registration/password reset links and intercepts manual navigation attempts to `/account/register` by redirecting to login with a warning. --------- Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Reviewed-on: #56 Co-authored-by: Antigravity <antigravity@google.com> Co-committed-by: Antigravity <antigravity@google.com>
165 lines
6.2 KiB
Plaintext
165 lines
6.2 KiB
Plaintext
@page "/serilog-demo"
|
|
@inject ILogger<SerilogDemo> Logger
|
|
@inject IJSRuntime JSRuntime
|
|
|
|
@if (_isDebug)
|
|
{
|
|
<div class="serilog-demo-container">
|
|
<div class="header-card glass-panel">
|
|
<div class="header-content">
|
|
<NexusIcon Name="cpu" Size="36" Class="header-icon" />
|
|
<div class="header-text">
|
|
<h1>Serilog Logging Infrastructure</h1>
|
|
<p class="subtitle">Production-grade diagnostic pipeline for unified native & web logs</p>
|
|
</div>
|
|
</div>
|
|
<div class="status-badge">
|
|
<span class="status-dot green"></span>
|
|
<span class="status-text">Pipeline Active</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="demo-grid">
|
|
<!-- Native .NET Logging Panel -->
|
|
<div class="control-card glass-panel">
|
|
<div class="card-header">
|
|
<NexusIcon Name="terminal" Size="20" Class="card-icon" />
|
|
<h2>Native .NET Logs (C#)</h2>
|
|
</div>
|
|
<p class="card-desc">Trigger structured C# logs using Dependency Injected ILogger.</p>
|
|
<div class="btn-group">
|
|
<button class="btn btn-info" @onclick="LogInfo">
|
|
<NexusIcon Name="info" Size="16" />
|
|
Log Info
|
|
</button>
|
|
<button class="btn btn-warning" @onclick="LogWarning">
|
|
<NexusIcon Name="alert-triangle" Size="16" />
|
|
Log Warning
|
|
</button>
|
|
<button class="btn btn-error" @onclick="LogError">
|
|
<NexusIcon Name="x-circle" Size="16" />
|
|
Log Error Exception
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Blazor / JS Interop Bridge Panel -->
|
|
<div class="control-card glass-panel">
|
|
<div class="card-header">
|
|
<NexusIcon Name="globe" Size="20" Class="card-icon js-icon" />
|
|
<h2>Blazor / JS WebView Logs</h2>
|
|
</div>
|
|
<p class="card-desc">Trigger logs from JavaScript to verify the interop error capture bridge.</p>
|
|
<div class="btn-group">
|
|
<button class="btn btn-js-info" @onclick="TriggerJsLog">
|
|
<NexusIcon Name="message-square" Size="16" />
|
|
Trigger console.log()
|
|
</button>
|
|
<button class="btn btn-js-error" @onclick="TriggerJsException">
|
|
<NexusIcon Name="zap" Size="16" />
|
|
Trigger JS Exception
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Log Config Panel -->
|
|
<div class="config-card glass-panel">
|
|
<div class="card-header">
|
|
<NexusIcon Name="settings" Size="20" Class="card-icon" />
|
|
<h2>Pipeline Diagnostics</h2>
|
|
</div>
|
|
<div class="config-grid">
|
|
<div class="config-item">
|
|
<span class="label">Rolling Daily File Sandbox Path</span>
|
|
<span class="value code-value">AppDataDirectory/logs/log-*.txt</span>
|
|
</div>
|
|
<div class="config-item">
|
|
<span class="label">Active Configuration Provider</span>
|
|
<span class="value">Serilog.Settings.Configuration (appsettings.json)</span>
|
|
</div>
|
|
<div class="config-item">
|
|
<span class="label">Native Apple Console Sink</span>
|
|
<span class="value">Serilog.Sinks.Debug (conditional compilation)</span>
|
|
</div>
|
|
<div class="config-item">
|
|
<span class="label">Native Android Logcat Sink</span>
|
|
<span class="value">AndroidLogcatSink (direct JNI bindings)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="serilog-demo-container">
|
|
<div class="glass-panel" style="text-align: center; padding: 3rem;">
|
|
<h2>Diagnostics Unavailable</h2>
|
|
<p>This page is only available in DEBUG builds.</p>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
// Compile-time check ensures _isDebug is baked as false in Release/Test/Production builds,
|
|
// which completely bypasses/strips rendering of the diagnostic UI and avoids exposing internal controls.
|
|
#if DEBUG
|
|
private readonly bool _isDebug = true;
|
|
#else
|
|
private readonly bool _isDebug = false;
|
|
#endif
|
|
|
|
private void LogInfo()
|
|
{
|
|
Logger.LogInformation("Structured native log triggered by user from SerilogDemo. Button: LogInfo");
|
|
}
|
|
|
|
private void LogWarning()
|
|
{
|
|
Logger.LogWarning("Potential warning log triggered from Blazor razor component at {Time}", DateTime.UtcNow);
|
|
}
|
|
|
|
private void LogError()
|
|
{
|
|
try
|
|
{
|
|
throw new InvalidOperationException("Simulated native C# operation exception triggered in Diagnostic dashboard.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Captured exception successfully in native Serilog pipeline!");
|
|
}
|
|
}
|
|
|
|
private async Task TriggerJsLog()
|
|
{
|
|
try
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("console.log", "Intercepted JS console statement from Blazor WebView interop trigger!");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogWarning(ex, "Failed to execute console.log from diagnostic panel.");
|
|
}
|
|
}
|
|
|
|
private async Task TriggerJsException()
|
|
{
|
|
try
|
|
{
|
|
// Triggers a TypeError by invoking a non-existent method, which is completely CSP-compliant and works without eval()
|
|
await JSRuntime.InvokeVoidAsync("window.nonExistentFunctionTriggeringException");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Simulated runtime JS Exception triggered and captured in Blazor UI");
|
|
try
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("console.error", $"Simulated JS Exception: {ex.Message}");
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
}
|
|
|