Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 84 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ dotnet_diagnostic.CA1051.severity = none # Do not declare visible instance field
dotnet_diagnostic.CA1852.severity = none # Type can be sealed
dotnet_diagnostic.CA1507.severity = none # Use nameof in place of string literal

# Suppress additional rules identified after .NET Aspire 13.2.1 Update
dotnet_diagnostic.CA2007.severity = none # Consider calling ConfigureAwait
dotnet_diagnostic.CA1032.severity = none # Implement standard exception constructors
dotnet_diagnostic.IDE0076.severity = none # Invalid or missing target for SuppressMessageAttribute
dotnet_diagnostic.CA1056.severity = none # Uri vs String
dotnet_diagnostic.CA1724.severity = none # Type name conflicts with namespace
dotnet_diagnostic.CA2225.severity = none # Operator alternates
dotnet_diagnostic.CA1040.severity = none # Avoid empty interfaces
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure
dotnet_diagnostic.CA1063.severity = suggestion # IDisposable implementation pattern (Critical - set to suggestion for local handling)
dotnet_diagnostic.CA2227.severity = suggestion # Collection properties should be read-only (Critical - set to suggestion for local handling)
dotnet_diagnostic.CA1034.severity = none # Do not nest types
dotnet_diagnostic.CA1002.severity = none # Use Collection<T> instead of List<T>
dotnet_diagnostic.CA1052.severity = none # Static holder types should be static
dotnet_diagnostic.CA1308.severity = none # ToLowerInvariant vs ToUpperInvariant
dotnet_diagnostic.IDE1006.severity = none # Naming rule violations
dotnet_diagnostic.IDE0056.severity = none # Indexing can be simplified
dotnet_diagnostic.IDE0301.severity = none # Collection initialization simplification
dotnet_diagnostic.IDE0305.severity = none # Collection initialization simplification
dotnet_diagnostic.IDE0023.severity = none # Use block body for expression-bodied members
dotnet_diagnostic.CA1031.severity = suggestion # Catch specific exceptions (Critical - set to suggestion for local handling)
dotnet_diagnostic.CA1851.severity = suggestion # Multiple enumeration of IEnumerable (Critical - set to suggestion for local handling)
dotnet_diagnostic.IDE0057.severity = none # Substring can be simplified

Comment thread
frigini marked this conversation as resolved.
# Technical Excellence Sprint Suppressions (Avoiding source code pollution)
# S2068 (Hardcoded passwords) and S3267 (LINQ simplification) have been removed from global suppressions.
# They should be managed with inline `#pragma` warnings where strictly necessary.
Expand All @@ -68,7 +92,7 @@ dotnet_diagnostic.CA1707.severity = none # Identifiers should not contain unders
dotnet_diagnostic.CA1822.severity = none # Mark members as static

# Default severity for all analyzers (Global fallback)
dotnet_analyzer_diagnostic.severity = warning
dotnet_analyzer_diagnostic.severity = suggestion

# Seeding-specific rules
[src/Shared/Seeding/**.cs]
Expand All @@ -81,3 +105,62 @@ dotnet_diagnostic.CS8601.severity = none
# S1186: Methods should not be empty
dotnet_diagnostic.S1186.severity = none

# Scoped Suppressions for Justified Warnings
[src/Shared/Monitoring/BusinessMetrics.cs]
dotnet_diagnostic.CA1063.severity = none

[src/Shared/Messaging/DeadLetter/DeadLetterStatistics.cs]
dotnet_diagnostic.CA2227.severity = none

[src/Shared/Messaging/DeadLetter/FailedMessageInfo.cs]
dotnet_diagnostic.CA2227.severity = none

[src/Shared/Messaging/Options/RabbitMqOptions.cs]
dotnet_diagnostic.CA2227.severity = none

[src/Shared/Authorization/Services/PermissionService.cs]
dotnet_diagnostic.CA1851.severity = none
dotnet_diagnostic.CA1031.severity = none

[src/Shared/Caching/HybridCacheService.cs]
dotnet_diagnostic.CA1031.severity = none

[src/Shared/Database/SchemaPermissionsManager.cs]
dotnet_diagnostic.CA1031.severity = none

[src/Shared/Events/EventDispatcher.cs]
dotnet_diagnostic.CA1031.severity = none

[src/Shared/Messaging/Handlers/MessageRetryMiddleware.cs]
dotnet_diagnostic.CA1031.severity = none

[src/Shared/Seeding/DevelopmentDataSeeder.cs]
# CA5394: Random is an insecure random number generator (acceptable for seeding)
dotnet_diagnostic.CA5394.severity = none

[src/Shared/Utilities/Constants/**]
dotnet_diagnostic.CA1034.severity = none

[src/Shared/Messaging/**]
dotnet_diagnostic.CA1819.severity = none

[src/Shared/Caching/**]
dotnet_diagnostic.CA2000.severity = none
dotnet_diagnostic.CA1508.severity = none

[src/Shared/Database/**]
dotnet_diagnostic.CA2000.severity = none

[src/Shared/Functional/**]
dotnet_diagnostic.CA1805.severity = none

[src/Shared/Messaging/MessagingExtensions.cs]
dotnet_diagnostic.S2094.severity = none

[src/Modules/Providers/Application/Handlers/**]
dotnet_diagnostic.S1006.severity = none

# Global Enum zero exceptions
[src/Modules/Providers/Domain/Enums/{EDocumentType,EProviderType,EVerificationStatus}.cs]
dotnet_diagnostic.CA1008.severity = none

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ logs/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/*.txt

# IDE
.idea/
Expand Down
45 changes: 21 additions & 24 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageVersion Include="HtmlSanitizer" Version="9.0.892" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.5" />
<!-- .NET & Microsoft Core -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<!-- Microsoft.Build packages - updated to 18.0.2 (CVE-2025-55247 resolved) -->
<PackageVersion Include="Microsoft.Build" Version="18.0.2" />
Expand Down Expand Up @@ -95,9 +95,9 @@
<PackageVersion Include="Microsoft.FeatureManagement.AspNetCore" Version="4.4.0" />
<!-- OpenTelemetry -->
<PackageVersion Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.4.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.14.0-beta.2" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
Expand Down Expand Up @@ -143,15 +143,15 @@
<!-- System.IO.Hashing: Version 9.0.10 required by Aspire.Hosting.Azure.AppContainers 13.0.2 -->
<!-- Note: Azure.Storage.Common 12.25.0 depends on 8.0.0, but NuGet resolves to 9.0.10 via transitive lift -->
<!-- This is acceptable as System.IO.Hashing maintains backward compatibility -->
<PackageVersion Include="System.IO.Hashing" Version="10.0.1" />
<PackageVersion Include="System.IO.Hashing" Version="10.0.5" />
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
<!-- IdentityModel family - aligned to 8.16.0 to avoid version skew -->
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.16.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols" Version="8.16.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.16.0" />
<!-- Testing Framework -->
<!-- Using xUnit v3 only -->
<PackageVersion Include="WireMock.Net" Version="2.0.0" />
<PackageVersion Include="WireMock.Net" Version="2.2.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="8.0.1" />
Expand All @@ -174,35 +174,32 @@
<!-- Projects use Npgsql.EntityFrameworkCore.PostgreSQL directly (10.0.0) -->
<!-- AppHost uses Aspire.Hosting.PostgreSQL for orchestration (not this package) -->
<!-- Tested: Build + Integration tests pass without this Aspire wrapper -->
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Azure.PostgreSQL" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.JavaScript" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.1.3" />
<!-- Aspire.Hosting.Keycloak: NO STABLE VERSION EXISTS - using latest preview -->
<!-- Nearest available: 13.1.0-preview.1.25616.3 (updated from 13.0.2-preview.1.25603.5) -->
<!-- MONITORING: Check https://www.nuget.org/packages/Aspire.Hosting.Keycloak for stable release -->
<!-- DECISION: Package will remain in preview until Keycloak integration stabilizes in Aspire -->
<PackageVersion Include="Aspire.Hosting.Keycloak" Version="13.1.0-preview.1.25616.3" />
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.RabbitMQ" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="13.2.1" />
<PackageVersion Include="Aspire.Hosting.Azure.PostgreSQL" Version="13.2.1" />
<PackageVersion Include="Aspire.Hosting.JavaScript" Version="13.2.1" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.2.1" />
<!-- Aspire.Hosting.Keycloak: Updated to 13.2.1-preview.1.26180.6 (Nearest version for Aspire 13.2.1) -->
<PackageVersion Include="Aspire.Hosting.Keycloak" Version="13.2.1-preview.1.26180.6" />
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="13.2.1" />
<PackageVersion Include="Aspire.Hosting.RabbitMQ" Version="13.2.1" />
<!-- Aspire.Hosting.Redis: Updated to 13.1.0 (2026-02-05) -->
<!-- TLS bug from early 13.1.0 release was FIXED in late 2025 -->
<!-- TESTED: 13.1.0 builds and runs without SSL Handshake errors -->
<!-- HTTPS termination is now opt-in (not forced) -->
<PackageVersion Include="Aspire.Hosting.Redis" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Seq" Version="13.1.3" />
<PackageVersion Include="Aspire.Npgsql" Version="13.1.3" />
<PackageVersion Include="Aspire.StackExchange.Redis" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="13.2.1" />
<PackageVersion Include="Aspire.Hosting.Seq" Version="13.2.1" />
<PackageVersion Include="Aspire.Npgsql" Version="13.2.1" />
<PackageVersion Include="Aspire.StackExchange.Redis" Version="13.2.1" />
<!-- Architecture Testing -->
<PackageVersion Include="NetArchTest.Rules" Version="1.3.2" />
<!-- Code Analysis -->
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.21.0.135717" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.22.0.136894" />
<!-- Blazor UI & State Management -->
<PackageVersion Include="MudBlazor" Version="8.15.0" />
<PackageVersion Include="Fluxor.Blazor.Web" Version="6.9.0" />
<PackageVersion Include="Fluxor.Blazor.Web.ReduxDevTools" Version="6.9.0" />
<PackageVersion Include="Refit" Version="10.0.1" />
<PackageVersion Include="Refit.HttpClientFactory" Version="10.0.1" />
<PackageVersion Include="Refit" Version="10.1.6" />
<PackageVersion Include="Refit.HttpClientFactory" Version="10.1.6" />
<!-- Blazor Testing -->
<PackageVersion Include="bUnit" Version="2.6.2" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"rollForward": "latestMinor"
},
"msbuild-sdks": {
"Aspire.AppHost.Sdk": "13.1.3",
"Aspire.AppHost.Sdk": "13.2.1",
"Microsoft.Build.NoTargets": "3.7.134"
}
}
3 changes: 0 additions & 3 deletions src/Aspire/MeAjudaAi.AppHost/Extensions/KeycloakExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ public static MeAjudaAiKeycloakResult AddMeAjudaAiKeycloak(
// Usar porta fixa para permitir acesso consistente em desenvolvimento
// NOTA: Sem .WithDataVolume() em desenvolvimento para sempre iniciar limpo
// NOTA: Keycloak pode aparecer como Unhealthy ~20-30s até completar inicialização do banco e import de realms
// ⚠️ LIMITAÇÃO: Aspire.Hosting.Keycloak NÃO suporta health checks (docs oficiais confirmam)
// WaitFor(keycloak) só espera container Running, não Keycloak HTTP ready
// Por isso removemos .WaitFor() no Program.cs - serviços iniciam sem esperar Keycloak
var keycloak = builder.AddKeycloak("keycloak", port: 8080)
// Configurar banco de dados PostgreSQL com schema 'identity'
// Na rede Docker do Aspire, containers se comunicam usando o nome do recurso
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire/MeAjudaAi.AppHost/MeAjudaAi.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<Project Sdk="Aspire.AppHost.Sdk/13.1.0">
<Project Sdk="Aspire.AppHost.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>6a599a9f-ccdf-4f96-b355-3aeec60dd18b</UserSecretsId>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<!-- Disable locked mode due to platform-specific Aspire.Dashboard.Sdk dependencies -->
<RestoreLockedMode>false</RestoreLockedMode>
</PropertyGroup>
Expand Down
37 changes: 25 additions & 12 deletions src/Aspire/MeAjudaAi.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ internal static class Program
public static void Main(string[] args)
{
var builder = DistributedApplication.CreateBuilder(args);

// Recurso sentinela para sincronização da criação de clientes no Keycloak
var keycloakBootstrap = builder.AddResource(new Resources.KeycloakBootstrapResource("keycloak-clients-ready"))
.ExcludeFromManifest()
.WithInitialState(new CustomResourceSnapshot
{
ResourceType = "Sentinels",
State = new ResourceStateSnapshot("Waiting", "gray"),
Properties = [new ResourcePropertySnapshot("Status", "Waiting for Keycloak Bootstrap...")]
});

// Registra o serviço em segundo plano Keycloak Bootstrap
if (EnvironmentHelpers.IsDevelopment(builder))
Expand All @@ -32,12 +42,12 @@ public static void Main(string[] args)
else if (EnvironmentHelpers.IsDevelopment(builder))
{
Console.WriteLine("⚙️ Configuring DEVELOPMENT environment");
ConfigureDevelopmentEnvironment(builder);
ConfigureDevelopmentEnvironment(builder, keycloakBootstrap);
}
else if (EnvironmentHelpers.IsProduction(builder))
{
Console.WriteLine("⚙️ Configuring PRODUCTION environment");
ConfigureProductionEnvironment(builder);
ConfigureProductionEnvironment(builder, keycloakBootstrap);
}
else
{
Expand Down Expand Up @@ -98,7 +108,7 @@ private static void ConfigureTestingEnvironment(IDistributedApplicationBuilder b
.WithEnvironment("HealthChecks__Timeout", "30");
}

private static void ConfigureDevelopmentEnvironment(IDistributedApplicationBuilder builder)
private static void ConfigureDevelopmentEnvironment(IDistributedApplicationBuilder builder, IResourceBuilder<Resources.KeycloakBootstrapResource> keycloakBootstrap)
{
var mainDatabase = Environment.GetEnvironmentVariable("MAIN_DATABASE") ?? "meajudaai";
var dbUsername = Environment.GetEnvironmentVariable("DB_USERNAME") ?? "postgres";
Expand Down Expand Up @@ -196,9 +206,8 @@ void AddSocialProviderEnv(string providerName, string clientIdKey, string client
.WithReference(rabbitMq)
.WaitFor(rabbitMq)
.WithReference(keycloak.Keycloak)
// NOTA: Keycloak health check removido devido a bug em Aspire.Hosting.Keycloak 13.1.0-preview
// Health endpoint está em HTTPS na porta 9000 mas Aspire tenta HTTP, causando falha permanente
// Serviços devem implementar retry interno para aguardar Keycloak ficar disponível
.WaitFor(keycloak.Keycloak)
Comment thread
frigini marked this conversation as resolved.
.WaitFor(keycloakBootstrap)
.WithEnvironment("ASPNETCORE_ENVIRONMENT", EnvironmentHelpers.GetEnvironmentName(builder));

// Admin Portal (Next.js 15 React)
Expand All @@ -212,8 +221,9 @@ void AddSocialProviderEnv(string providerName, string clientIdKey, string client
.WithHttpEndpoint(port: 3002, env: "PORT")
.WithExternalHttpEndpoints()
.WithEnvironment("NEXT_PUBLIC_API_URL", apiService.GetEndpoint("http"))
.WaitFor(apiService);
// NOTA: Keycloak WaitFor removido - veja comentário no apiService acima
.WaitFor(apiService)
.WaitFor(keycloak.Keycloak)
.WaitFor(keycloakBootstrap);

// Aplicação Web do Cliente (Next.js 15)
var customerWebPath = Path.Combine(builder.AppHostDirectory, "..", "..", "..", "src", "Web", "MeAjudaAi.Web.Customer");
Expand All @@ -226,7 +236,8 @@ void AddSocialProviderEnv(string providerName, string clientIdKey, string client
.WithHttpEndpoint(port: 3000, env: "PORT")
.WithExternalHttpEndpoints()
.WithEnvironment("NEXT_PUBLIC_API_URL", apiService.GetEndpoint("http"))
.WaitFor(apiService);
.WaitFor(apiService)
.WaitFor(keycloakBootstrap);
// Nota: AddJavaScriptApp usa "dev" script por padrão em desenvolvimento
// e "build" script em produção. Ver package.json para scripts configurados.

Expand All @@ -241,15 +252,16 @@ void AddSocialProviderEnv(string providerName, string clientIdKey, string client
.WithHttpEndpoint(port: 3001, env: "PORT")
.WithExternalHttpEndpoints()
.WithEnvironment("NEXT_PUBLIC_API_URL", apiService.GetEndpoint("http"))
.WaitFor(apiService);
.WaitFor(apiService)
.WaitFor(keycloakBootstrap);

// Pass resolved endpoints to Keycloak options for bootstrap
keycloakSettings.AdminPortalEndpoint = adminPortal.GetEndpoint("http");
keycloakSettings.CustomerWebEndpoint = customerWeb.GetEndpoint("http");
keycloakSettings.ProviderWebEndpoint = providerWeb.GetEndpoint("http");
}

private static void ConfigureProductionEnvironment(IDistributedApplicationBuilder builder)
private static void ConfigureProductionEnvironment(IDistributedApplicationBuilder builder, IResourceBuilder<Resources.KeycloakBootstrapResource> keycloakBootstrap)
{
var postgresql = builder.AddMeAjudaAiAzurePostgreSQL(options =>
{
Expand All @@ -271,7 +283,8 @@ private static void ConfigureProductionEnvironment(IDistributedApplicationBuilde
.WithReference(rabbitMq)
.WaitFor(rabbitMq)
.WithReference(keycloak.Keycloak)
// NOTA: Keycloak WaitFor removido - veja comentário no ConfigureDevelopmentEnvironment
.WaitFor(keycloak.Keycloak)
.WaitFor(keycloakBootstrap)
.WithEnvironment("ASPNETCORE_ENVIRONMENT", EnvironmentHelpers.GetEnvironmentName(builder));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Aspire.Hosting.ApplicationModel;

namespace MeAjudaAi.AppHost.Resources;

/// <summary>
/// Recurso sentinela que representa a conclusão da configuração (bootstrap) do Keycloak.
/// Outros recursos podem dar um .WaitFor() neste recurso para garantir que os clientes OIDC existam.
/// </summary>
public class KeycloakBootstrapResource(string name) : Resource(name), IResource
{
}
Loading
Loading