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 @@
+