diff --git a/src/Accounts/Hearth.Accounts.Contracts/Hearth.Accounts.Contracts.csproj b/src/Accounts/Hearth.Accounts.Contracts/Hearth.Accounts.Contracts.csproj new file mode 100644 index 0000000..61718a1 --- /dev/null +++ b/src/Accounts/Hearth.Accounts.Contracts/Hearth.Accounts.Contracts.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Accounts/Hearth.Accounts/Hearth.Accounts.csproj b/src/Accounts/Hearth.Accounts/Hearth.Accounts.csproj new file mode 100644 index 0000000..6032ef1 --- /dev/null +++ b/src/Accounts/Hearth.Accounts/Hearth.Accounts.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Characters/Hearth.Characters.Contracts/Hearth.Characters.Contracts.csproj b/src/Characters/Hearth.Characters.Contracts/Hearth.Characters.Contracts.csproj new file mode 100644 index 0000000..61718a1 --- /dev/null +++ b/src/Characters/Hearth.Characters.Contracts/Hearth.Characters.Contracts.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Characters/Hearth.Characters/Hearth.Characters.csproj b/src/Characters/Hearth.Characters/Hearth.Characters.csproj new file mode 100644 index 0000000..52528c3 --- /dev/null +++ b/src/Characters/Hearth.Characters/Hearth.Characters.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Commons/Hearth.Commons/Hearth.Commons.csproj b/src/Commons/Hearth.Commons/Hearth.Commons.csproj new file mode 100644 index 0000000..61718a1 --- /dev/null +++ b/src/Commons/Hearth.Commons/Hearth.Commons.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..3de9aa2 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,10 @@ + + + + net10.0 + enable + enable + true + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..51ec396 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,15 @@ + + + true + true + + + + + + + + + + + diff --git a/src/External/Hearth.External/Hearth.External.csproj b/src/External/Hearth.External/Hearth.External.csproj new file mode 100644 index 0000000..61718a1 --- /dev/null +++ b/src/External/Hearth.External/Hearth.External.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Guilds/Hearth.Guilds.Contracts/Hearth.Guilds.Contracts.csproj b/src/Guilds/Hearth.Guilds.Contracts/Hearth.Guilds.Contracts.csproj new file mode 100644 index 0000000..61718a1 --- /dev/null +++ b/src/Guilds/Hearth.Guilds.Contracts/Hearth.Guilds.Contracts.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Guilds/Hearth.Guilds/Hearth.Guilds.csproj b/src/Guilds/Hearth.Guilds/Hearth.Guilds.csproj new file mode 100644 index 0000000..cbf5b0a --- /dev/null +++ b/src/Guilds/Hearth.Guilds/Hearth.Guilds.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Hearth.slnx b/src/Hearth.slnx new file mode 100644 index 0000000..19a0f6f --- /dev/null +++ b/src/Hearth.slnx @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Hosts/Hearth.Api/Hearth.Api.csproj b/src/Hosts/Hearth.Api/Hearth.Api.csproj new file mode 100644 index 0000000..49c94d3 --- /dev/null +++ b/src/Hosts/Hearth.Api/Hearth.Api.csproj @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/Hosts/Hearth.Api/Program.cs b/src/Hosts/Hearth.Api/Program.cs new file mode 100644 index 0000000..fa5c301 --- /dev/null +++ b/src/Hosts/Hearth.Api/Program.cs @@ -0,0 +1,11 @@ +using Hearth.Hosting; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddHostDefaults(); + +var app = builder.Build(); + +app.MapHostEndpoints(); + +app.Run(); diff --git a/src/Hosts/Hearth.Api/Properties/launchSettings.json b/src/Hosts/Hearth.Api/Properties/launchSettings.json new file mode 100644 index 0000000..804e684 --- /dev/null +++ b/src/Hosts/Hearth.Api/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "http://localhost:5080", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Hosts/Hearth.Api/appsettings.json b/src/Hosts/Hearth.Api/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/Hosts/Hearth.Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Hosts/Hearth.AppHost/AppHost.cs b/src/Hosts/Hearth.AppHost/AppHost.cs new file mode 100644 index 0000000..94e40ed --- /dev/null +++ b/src/Hosts/Hearth.AppHost/AppHost.cs @@ -0,0 +1,5 @@ +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddProject("api"); + +builder.Build().Run(); diff --git a/src/Hosts/Hearth.AppHost/Hearth.AppHost.csproj b/src/Hosts/Hearth.AppHost/Hearth.AppHost.csproj new file mode 100644 index 0000000..1c0bb5c --- /dev/null +++ b/src/Hosts/Hearth.AppHost/Hearth.AppHost.csproj @@ -0,0 +1,13 @@ + + + + Exe + true + 388fbdf7-df04-4c75-858e-1bdc720d2a91 + + + + + + + diff --git a/src/Hosts/Hearth.AppHost/Properties/launchSettings.json b/src/Hosts/Hearth.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..f0e1b3d --- /dev/null +++ b/src/Hosts/Hearth.AppHost/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:17277;http://localhost:15247", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21265", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22059" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:15247", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19112", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20192", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + } + } +} diff --git a/src/Hosts/Hearth.AppHost/appsettings.json b/src/Hosts/Hearth.AppHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/src/Hosts/Hearth.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/Hosts/Hearth.AppHost/aspire.config.json b/src/Hosts/Hearth.AppHost/aspire.config.json new file mode 100644 index 0000000..e03be6b --- /dev/null +++ b/src/Hosts/Hearth.AppHost/aspire.config.json @@ -0,0 +1,5 @@ +{ + "appHost": { + "path": "Hearth.AppHost.csproj" + } +} diff --git a/src/Hosts/Hearth.Hosting/Hearth.Hosting.csproj b/src/Hosts/Hearth.Hosting/Hearth.Hosting.csproj new file mode 100644 index 0000000..4380514 --- /dev/null +++ b/src/Hosts/Hearth.Hosting/Hearth.Hosting.csproj @@ -0,0 +1,21 @@ + + + + true + + + + + + + + + + + + + + + + + diff --git a/src/Hosts/Hearth.Hosting/HostingExtensions.cs b/src/Hosts/Hearth.Hosting/HostingExtensions.cs new file mode 100644 index 0000000..2b6658c --- /dev/null +++ b/src/Hosts/Hearth.Hosting/HostingExtensions.cs @@ -0,0 +1,85 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Hearth.Hosting; + +// Setup every Hearth host needs the same way: logging and telemetry, health endpoints, service discovery and HTTP resilience. +// Only hosts reference this project; anything a single host or a module needs belongs elsewhere. +public static class HostingExtensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + private const string LiveTag = "live"; + + public static TBuilder AddHostDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + builder.Services.ConfigureHttpClientDefaults(http => + { + http.AddStandardResilienceHandler(); + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + var openTelemetry = builder.Services + .AddOpenTelemetry() + .WithMetrics(metrics => metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation()) + .WithTracing(tracing => tracing + .AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(options => options.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)) + .AddHttpClientInstrumentation()); + + if (!string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"])) + { + openTelemetry.UseOtlpExporter(); + } + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services + .AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy(), [LiveTag]); + + return builder; + } + + // Mapped in every environment, unlike the Aspire template: the Kubernetes probes need them in production. + public static WebApplication MapHostEndpoints(this WebApplication app) + { + app.MapHealthChecks(HealthEndpointPath); + app.MapHealthChecks( + AlivenessEndpointPath, + new HealthCheckOptions { Predicate = registration => registration.Tags.Contains(LiveTag) } + ); + + return app; + } +} diff --git a/src/global.json b/src/global.json new file mode 100644 index 0000000..1d364c6 --- /dev/null +++ b/src/global.json @@ -0,0 +1,9 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + }, + "test": { + "runner": "Microsoft.Testing.Platform" + } +}