diff --git a/.aspire/settings.json b/.aspire/settings.json new file mode 100644 index 000000000..543eb1bdf --- /dev/null +++ b/.aspire/settings.json @@ -0,0 +1,6 @@ +{ + "appHostPath": "../Valour.AppHost/Valour.AppHost.csproj", + "features": { + "execCommandEnabled": "true" + } +} \ No newline at end of file diff --git a/Config/Properties/launchSettings.json b/Config/Properties/launchSettings.json new file mode 100644 index 000000000..08bab548e --- /dev/null +++ b/Config/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Valour.Config": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:51199;http://localhost:51200" + } + } +} \ No newline at end of file diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 000000000..993d13d07 --- /dev/null +++ b/NuGet.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Valour.AppHost/AppHost.cs b/Valour.AppHost/AppHost.cs new file mode 100644 index 000000000..d17910fbb --- /dev/null +++ b/Valour.AppHost/AppHost.cs @@ -0,0 +1,22 @@ +using Projects; + +var builder = DistributedApplication.CreateBuilder(args); + +var postgres = builder.AddPostgres("valourgres") + .WithImageTag("16.12") // pin version + .WithDataVolume("valour-postgres-data") + .WithPgAdmin(); + +var valourDb = postgres.AddDatabase("valourdb"); + +var redis = builder.AddRedis("redis") + .WithDataVolume("redis-data") + .WithRedisInsight(); + +var valourServer = builder.AddProject("valour-server") + .WaitFor(valourDb) + .WaitFor(redis) + .WithReference(valourDb) + .WithReference(redis); + +builder.Build().Run(); diff --git a/Valour.AppHost/Properties/launchSettings.json b/Valour.AppHost/Properties/launchSettings.json new file mode 100644 index 000000000..029bfe856 --- /dev/null +++ b/Valour.AppHost/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17166;http://localhost:15103", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21027", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23074", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22220" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15103", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19169", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18170", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20155" + } + } + } +} diff --git a/Valour.AppHost/Valour.AppHost.csproj b/Valour.AppHost/Valour.AppHost.csproj new file mode 100644 index 000000000..aadc02a4d --- /dev/null +++ b/Valour.AppHost/Valour.AppHost.csproj @@ -0,0 +1,20 @@ + + + + + + + + + + + + + Exe + net10.0 + enable + enable + 52e455ab-3d66-4aa9-ab01-ccf4ee55a916 + + + diff --git a/Valour.AppHost/appsettings.Development.json b/Valour.AppHost/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/Valour.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Valour.ServiceDefaults/Extensions.cs b/Valour.ServiceDefaults/Extensions.cs new file mode 100644 index 000000000..b72c8753c --- /dev/null +++ b/Valour.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/Valour.ServiceDefaults/Valour.ServiceDefaults.csproj b/Valour.ServiceDefaults/Valour.ServiceDefaults.csproj new file mode 100644 index 000000000..eeb71e38f --- /dev/null +++ b/Valour.ServiceDefaults/Valour.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/Valour.sln b/Valour.sln index d495528fc..7b01a2834 100644 --- a/Valour.sln +++ b/Valour.sln @@ -26,48 +26,152 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Valour.Config", "Config\Val EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Valour.Client.Maui", "Valour\Client.Maui\Valour.Client.Maui.csproj", "{C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Valour.AppHost", "Valour.AppHost\Valour.AppHost.csproj", "{6B942C37-FE38-4DF7-BC0E-451C34CC799C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Valour.ServiceDefaults", "Valour.ServiceDefaults\Valour.ServiceDefaults.csproj", "{B35FB75E-5242-441D-BCBC-B769A5F78808}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|x64.ActiveCfg = Debug|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|x64.Build.0 = Debug|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|x86.ActiveCfg = Debug|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Debug|x86.Build.0 = Debug|Any CPU {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|Any CPU.ActiveCfg = Release|Any CPU {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|Any CPU.Build.0 = Release|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|x64.ActiveCfg = Release|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|x64.Build.0 = Release|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|x86.ActiveCfg = Release|Any CPU + {17DA107C-C64F-4AE9-9FDA-EF6532139659}.Release|x86.Build.0 = Release|Any CPU {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|x64.Build.0 = Debug|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Debug|x86.Build.0 = Debug|Any CPU {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|Any CPU.Build.0 = Release|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|x64.ActiveCfg = Release|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|x64.Build.0 = Release|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|x86.ActiveCfg = Release|Any CPU + {D3B85CB7-3EC8-4180-8F5C-73C09C2A1D7F}.Release|x86.Build.0 = Release|Any CPU {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|x64.ActiveCfg = Debug|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|x64.Build.0 = Debug|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|x86.ActiveCfg = Debug|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Debug|x86.Build.0 = Debug|Any CPU {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|Any CPU.Build.0 = Release|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|x64.ActiveCfg = Release|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|x64.Build.0 = Release|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|x86.ActiveCfg = Release|Any CPU + {9CABBEEF-DE8F-4374-9818-A55B62503004}.Release|x86.Build.0 = Release|Any CPU {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|x64.Build.0 = Debug|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Debug|x86.Build.0 = Debug|Any CPU {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|Any CPU.Build.0 = Release|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|x64.ActiveCfg = Release|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|x64.Build.0 = Release|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|x86.ActiveCfg = Release|Any CPU + {D5C0CB27-80FC-415C-85DC-E800207166C8}.Release|x86.Build.0 = Release|Any CPU {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|x64.ActiveCfg = Debug|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|x64.Build.0 = Debug|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|x86.ActiveCfg = Debug|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Debug|x86.Build.0 = Debug|Any CPU {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|Any CPU.Build.0 = Release|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|x64.ActiveCfg = Release|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|x64.Build.0 = Release|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|x86.ActiveCfg = Release|Any CPU + {0A2EBA6C-8804-4B28-9697-24DF8E744EE6}.Release|x86.Build.0 = Release|Any CPU {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|x64.ActiveCfg = Debug|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|x64.Build.0 = Debug|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Debug|x86.Build.0 = Debug|Any CPU {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|Any CPU.ActiveCfg = Release|Any CPU {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|Any CPU.Build.0 = Release|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|x64.ActiveCfg = Release|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|x64.Build.0 = Release|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|x86.ActiveCfg = Release|Any CPU + {B192A998-9AED-40F7-BF3D-0BF7D060BEAD}.Release|x86.Build.0 = Release|Any CPU {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|x64.Build.0 = Debug|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|x86.ActiveCfg = Debug|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Debug|x86.Build.0 = Debug|Any CPU {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|Any CPU.Build.0 = Release|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|x64.ActiveCfg = Release|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|x64.Build.0 = Release|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|x86.ActiveCfg = Release|Any CPU + {E0ECFCA0-767D-4E71-B54D-66BB4C9BEEC9}.Release|x86.Build.0 = Release|Any CPU {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|x64.ActiveCfg = Debug|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|x64.Build.0 = Debug|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|x86.ActiveCfg = Debug|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Debug|x86.Build.0 = Debug|Any CPU {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|Any CPU.ActiveCfg = Release|Any CPU {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|Any CPU.Build.0 = Release|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|x64.ActiveCfg = Release|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|x64.Build.0 = Release|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|x86.ActiveCfg = Release|Any CPU + {6F86BE9F-D601-4330-81E2-C0B6C257B593}.Release|x86.Build.0 = Release|Any CPU {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|x64.ActiveCfg = Debug|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|x64.Build.0 = Debug|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|x86.ActiveCfg = Debug|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Debug|x86.Build.0 = Debug|Any CPU {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|Any CPU.Build.0 = Release|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|x64.ActiveCfg = Release|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|x64.Build.0 = Release|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|x86.ActiveCfg = Release|Any CPU + {C1A6427C-433D-40D0-9C6A-F3BC2F3B46F8}.Release|x86.Build.0 = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|x64.Build.0 = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Debug|x86.Build.0 = Debug|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|Any CPU.Build.0 = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|x64.ActiveCfg = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|x64.Build.0 = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|x86.ActiveCfg = Release|Any CPU + {6B942C37-FE38-4DF7-BC0E-451C34CC799C}.Release|x86.Build.0 = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|x64.ActiveCfg = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|x64.Build.0 = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|x86.ActiveCfg = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Debug|x86.Build.0 = Debug|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|Any CPU.Build.0 = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|x64.ActiveCfg = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|x64.Build.0 = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|x86.ActiveCfg = Release|Any CPU + {B35FB75E-5242-441D-BCBC-B769A5F78808}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Valour/Client.Blazor/Program.cs b/Valour/Client.Blazor/Program.cs index 4302abc30..b17100f24 100644 --- a/Valour/Client.Blazor/Program.cs +++ b/Valour/Client.Blazor/Program.cs @@ -17,20 +17,25 @@ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); - + builder.UseSentry(options => { options.Dsn = "https://6cfc20b598b8831b69f8a30629325213@o4510867505479680.ingest.us.sentry.io/4510869629435904"; options.MinimumEventLevel = LogLevel.Error; options.SetBeforeSend((e, _) => SentryGate.IsEnabled ? e : null); }); - + builder.Services.AddSingleton(); builder.Services.AddSingleton(); - // Default to the API host. Web deploys can override at runtime via valour-runtime-config.js. - builder.Services.AddValourClientServices("https://api.valour.gg"); - - var host = builder.Build(); + +#if DEBUG + + var apiBaseAddress = builder.HostEnvironment.BaseAddress; + builder.Services.AddValourClientServices(apiBaseAddress); +#else + builder.Services.AddValourClientServices("https://api.valour.gg"); +#endif + var host = builder.Build(); await host.RunAsync(); } } diff --git a/Valour/Client.Maui/Valour.Client.Maui.csproj b/Valour/Client.Maui/Valour.Client.Maui.csproj index 68de7d503..85d950784 100644 --- a/Valour/Client.Maui/Valour.Client.Maui.csproj +++ b/Valour/Client.Maui/Valour.Client.Maui.csproj @@ -90,6 +90,10 @@ + + + + diff --git a/Valour/Database/Context/ValourDB.cs b/Valour/Database/Context/ValourDB.cs index 57171982e..f587a97a9 100644 --- a/Valour/Database/Context/ValourDB.cs +++ b/Valour/Database/Context/ValourDB.cs @@ -248,8 +248,12 @@ public ValourDb(DbContextOptions options) : base(options) protected override void OnConfiguring(DbContextOptionsBuilder options) { + if (options.IsConfigured) + return; + options.ConfigureWarnings(w => w.Ignore(RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables)); - options.UseNpgsql(ConnectionString).UseExceptionProcessor(); + options.UseNpgsql(ConnectionString); + options.UseExceptionProcessor(); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/Valour/Server/Program.cs b/Valour/Server/Program.cs index 83057c32b..b1d465ab5 100644 --- a/Valour/Server/Program.cs +++ b/Valour/Server/Program.cs @@ -6,6 +6,8 @@ using System.Text.Json; using Amazon; using CloudFlare.Client; +using EntityFramework.Exceptions.PostgreSQL; +using Microsoft.EntityFrameworkCore.Diagnostics; using StackExchange.Redis; using Valour.Server.API; using Valour.Server.Cdn; @@ -43,6 +45,19 @@ public static async Task Main(string[] args) // Load configs ConfigLoader.LoadConfigs(); +#if DEBUG + NodeConfig.Instance ??= new NodeConfig + { + Name = "dev-node", + Location = "localhost", + LogInfo = true + }; + // Aspire specific + builder.AddServiceDefaults(); +#endif + + // Initialize Email Manager + EmailManager.SetupClient(); // Initialize Stripe if (StripeConfig.Current?.SecretKey is not null) Stripe.StripeConfiguration.ApiKey = StripeConfig.Current.SecretKey; @@ -94,6 +109,11 @@ public static async Task Main(string[] args) // Build web app var app = builder.Build(); + // Initialize database schema + using var scope = app.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.Migrate(); + // Configure application ConfigureApp(app); @@ -282,24 +302,25 @@ public static void ConfigureServices(WebApplicationBuilder builder) options.MemoryBufferThreshold = 20_971_520; // 20 MB memory buffer before spilling to disk options.MultipartBodyLengthLimit = 262_144_000; // 250 MB (max tier upload limit) }); +#if DEBUG + + builder.AddNpgsqlDbContext("valourdb", configureDbContextOptions: options => + { + options.ConfigureWarnings(w => w.Ignore(RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables)); + options.UseExceptionProcessor(); + // Disable retrying execution strategy to allow manual transactions + options.UseNpgsql(npgsqlOptions => npgsqlOptions.EnableRetryOnFailure(0)); + }); + builder.AddRedisClient("redis"); +#else + services.AddDbContext(options => { options.UseNpgsql(ValourDb.ConnectionString); }, ServiceLifetime.Scoped); + Console.WriteLine("Connecting to redis with connection string: " + RedisConfig.Current.ConnectionString?.Split(",")[0]); + services.AddSingleton( + ConnectionMultiplexer.Connect(RedisConfig.Current.ConnectionString)); +#endif - services.AddDbContext(options => { options.UseNpgsql(ValourDb.ConnectionString); }, ServiceLifetime.Scoped); - - // Apply migrations if flag is set - //if (Environment.GetEnvironmentVariable("APPLY_MIGRATIONS") == "true") - //{ - using var scope = services.BuildServiceProvider().CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); - db.Database.Migrate(); - //} - - Console.WriteLine("Connecting to redis with connection string: " + RedisConfig.Current.ConnectionString?.Split(",")[0]); - - services.AddSingleton( - ConnectionMultiplexer.Connect(RedisConfig.Current.ConnectionString)); - - // This probably needs to be customized further but the documentation changed - services.AddAuthentication().AddCookie(CookieAuthenticationDefaults.AuthenticationScheme); + // This probably needs to be customized further but the documentation changed + services.AddAuthentication().AddCookie(CookieAuthenticationDefaults.AuthenticationScheme); services.AddControllersWithViews().AddJsonOptions(options => diff --git a/Valour/Server/Valour.Server.csproj b/Valour/Server/Valour.Server.csproj index e757f0437..1f87b700e 100644 --- a/Valour/Server/Valour.Server.csproj +++ b/Valour/Server/Valour.Server.csproj @@ -15,6 +15,8 @@ + + @@ -48,6 +50,7 @@ +