From 091097a858625fef5f0ac318d12035ff684dd415 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Sep 2025 00:16:13 +0800 Subject: [PATCH 01/11] :recycle: Remove etcd, replace with asprie. Move infra to aspire. Disable gateway for now --- DysonNetwork.Control/AppHost.cs | 30 ++++ .../DysonNetwork.Control.csproj | 30 ++++ .../Properties/launchSettings.json | 29 ++++ DysonNetwork.Control/appsettings.json | 11 ++ .../DysonNetwork.Develop.csproj | 1 + DysonNetwork.Develop/Program.cs | 9 +- DysonNetwork.Develop/appsettings.json | 4 +- DysonNetwork.Drive/DysonNetwork.Drive.csproj | 1 + DysonNetwork.Drive/Program.cs | 10 +- DysonNetwork.Drive/appsettings.json | 4 +- .../Controllers/WellKnownController.cs | 2 +- .../DysonNetwork.Gateway.csproj | 1 + DysonNetwork.Gateway/Program.cs | 4 + .../Startup/ServiceCollectionExtensions.cs | 3 - DysonNetwork.Pass/DysonNetwork.Pass.csproj | 1 + DysonNetwork.Pass/Email/EmailService.cs | 2 - DysonNetwork.Pass/Program.cs | 12 +- .../Startup/MetricsConfiguration.cs | 40 ----- DysonNetwork.Pass/appsettings.json | 4 +- .../Connection/WebSocketService.cs | 59 ++----- DysonNetwork.Ring/DysonNetwork.Ring.csproj | 1 + DysonNetwork.Ring/Program.cs | 7 +- .../Services/PusherServiceGrpc.cs | 1 - .../Services/QueueBackgroundService.cs | 1 - DysonNetwork.Ring/appsettings.json | 4 +- .../DysonNetwork.ServiceDefaults.csproj | 26 +++ DysonNetwork.ServiceDefaults/Extensions.cs | 132 ++++++++++++++ DysonNetwork.Shared/Auth/Startup.cs | 29 +--- .../DysonNetwork.Shared.csproj | 7 +- .../Http/KestrelConfiguration.cs | 14 -- DysonNetwork.Shared/Proto/GrpcClientHelper.cs | 162 +----------------- .../Proto/{pusher.proto => ring.proto} | 0 .../Registry/GatewayReverseProxy.cs | 83 --------- .../Registry/RegistryHostedService.cs | 60 ------- .../Registry/ServiceInjectionHelper.cs | 120 ++----------- .../Registry/ServiceRegistry.cs | 55 ------ DysonNetwork.Shared/Registry/Startup.cs | 28 --- DysonNetwork.Shared/Stream/Connector.cs | 21 --- .../DysonNetwork.Sphere.csproj | 2 +- DysonNetwork.Sphere/Poll/PollController.cs | 1 - DysonNetwork.Sphere/Program.cs | 13 +- DysonNetwork.Sphere/Realm/RealmController.cs | 1 - .../Startup/MetricsConfiguration.cs | 40 ----- DysonNetwork.Sphere/appsettings.json | 4 +- DysonNetwork.sln | 12 ++ DysonNetwork.sln.DotSettings.user | 2 + 46 files changed, 360 insertions(+), 723 deletions(-) create mode 100644 DysonNetwork.Control/AppHost.cs create mode 100644 DysonNetwork.Control/DysonNetwork.Control.csproj create mode 100644 DysonNetwork.Control/Properties/launchSettings.json create mode 100644 DysonNetwork.Control/appsettings.json delete mode 100644 DysonNetwork.Pass/Startup/MetricsConfiguration.cs create mode 100644 DysonNetwork.ServiceDefaults/DysonNetwork.ServiceDefaults.csproj create mode 100644 DysonNetwork.ServiceDefaults/Extensions.cs rename DysonNetwork.Shared/Proto/{pusher.proto => ring.proto} (100%) delete mode 100644 DysonNetwork.Shared/Registry/GatewayReverseProxy.cs delete mode 100644 DysonNetwork.Shared/Registry/RegistryHostedService.cs delete mode 100644 DysonNetwork.Shared/Registry/ServiceRegistry.cs delete mode 100644 DysonNetwork.Shared/Registry/Startup.cs delete mode 100644 DysonNetwork.Shared/Stream/Connector.cs delete mode 100644 DysonNetwork.Sphere/Startup/MetricsConfiguration.cs diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs new file mode 100644 index 0000000..a431e7c --- /dev/null +++ b/DysonNetwork.Control/AppHost.cs @@ -0,0 +1,30 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var database = builder.AddPostgres("database"); +var cache = builder.AddConnectionString("cache"); +var queue = builder.AddNats("queue").WithJetStream(); + +var ring = builder.AddProject("ring") + .WithReference(database) + .WithReference(queue); +var pass = builder.AddProject("pass") + .WithReference(database) + .WithReference(cache) + .WithReference(queue) + .WithReference(ring); +builder.AddProject("drive") + .WithReference(database) + .WithReference(cache) + .WithReference(queue) + .WithReference(pass); +builder.AddProject("sphere") + .WithReference(database) + .WithReference(cache) + .WithReference(queue) + .WithReference(pass); +builder.AddProject("develop") + .WithReference(database) + .WithReference(cache) + .WithReference(pass); + +builder.Build().Run(); \ No newline at end of file diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj new file mode 100644 index 0000000..ed47623 --- /dev/null +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -0,0 +1,30 @@ + + + + + + Exe + net9.0 + enable + enable + a68b3195-a00d-40c2-b5ed-d675356b7cde + DysonNetwork.Control + + + + + + + + + + + + + + + + + + + diff --git a/DysonNetwork.Control/Properties/launchSettings.json b/DysonNetwork.Control/Properties/launchSettings.json new file mode 100644 index 0000000..ee4fd34 --- /dev/null +++ b/DysonNetwork.Control/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17025;http://localhost:15057", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21175", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22189" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15057", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19163", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20185" + } + } + } +} diff --git a/DysonNetwork.Control/appsettings.json b/DysonNetwork.Control/appsettings.json new file mode 100644 index 0000000..1bdb86e --- /dev/null +++ b/DysonNetwork.Control/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "cache": "localhost:6379" + } +} diff --git a/DysonNetwork.Develop/DysonNetwork.Develop.csproj b/DysonNetwork.Develop/DysonNetwork.Develop.csproj index 1577bf3..49cbd38 100644 --- a/DysonNetwork.Develop/DysonNetwork.Develop.csproj +++ b/DysonNetwork.Develop/DysonNetwork.Develop.csproj @@ -31,6 +31,7 @@ + diff --git a/DysonNetwork.Develop/Program.cs b/DysonNetwork.Develop/Program.cs index df62582..3dae1e0 100644 --- a/DysonNetwork.Develop/Program.cs +++ b/DysonNetwork.Develop/Program.cs @@ -1,17 +1,16 @@ using DysonNetwork.Develop; using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Http; -using DysonNetwork.Shared.Registry; using DysonNetwork.Develop.Startup; -using DysonNetwork.Shared.Stream; +using DysonNetwork.Shared.Registry; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + builder.ConfigureAppKestrel(builder.Configuration); -builder.Services.AddRegistryService(builder.Configuration); -builder.Services.AddStreamConnection(builder.Configuration); builder.Services.AddAppServices(builder.Configuration); builder.Services.AddAppAuthentication(); builder.Services.AddAppSwagger(); @@ -22,6 +21,8 @@ builder.Services.AddDriveService(); var app = builder.Build(); +app.MapDefaultEndpoints(); + using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); diff --git a/DysonNetwork.Develop/appsettings.json b/DysonNetwork.Develop/appsettings.json index 1d407a2..ba758b3 100644 --- a/DysonNetwork.Develop/appsettings.json +++ b/DysonNetwork.Develop/appsettings.json @@ -24,8 +24,6 @@ }, "Service": { "Name": "DysonNetwork.Develop", - "Url": "https://localhost:7192", - "ClientCert": "../Certificates/client.crt", - "ClientKey": "../Certificates/client.key" + "Url": "https://localhost:7192" } } diff --git a/DysonNetwork.Drive/DysonNetwork.Drive.csproj b/DysonNetwork.Drive/DysonNetwork.Drive.csproj index 6e54599..d77a199 100644 --- a/DysonNetwork.Drive/DysonNetwork.Drive.csproj +++ b/DysonNetwork.Drive/DysonNetwork.Drive.csproj @@ -66,6 +66,7 @@ + diff --git a/DysonNetwork.Drive/Program.cs b/DysonNetwork.Drive/Program.cs index eef5c20..91fe0c7 100644 --- a/DysonNetwork.Drive/Program.cs +++ b/DysonNetwork.Drive/Program.cs @@ -5,18 +5,18 @@ using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Http; using DysonNetwork.Shared.PageData; using DysonNetwork.Shared.Registry; -using DysonNetwork.Shared.Stream; using Microsoft.EntityFrameworkCore; using tusdotnet.Stores; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure Kestrel and server options builder.ConfigureAppKestrel(builder.Configuration, maxRequestBodySize: long.MaxValue); // Add application services -builder.Services.AddRegistryService(builder.Configuration); -builder.Services.AddStreamConnection(builder.Configuration); + builder.Services.AddAppServices(builder.Configuration); builder.Services.AddAppRateLimiting(); builder.Services.AddAppAuthentication(); @@ -39,6 +39,8 @@ builder.Services.AddTransient(); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Run database migrations using (var scope = app.Services.CreateScope()) { @@ -51,8 +53,6 @@ var tusDiskStore = app.Services.GetRequiredService(); // Configure application middleware pipeline app.ConfigureAppMiddleware(tusDiskStore, builder.Environment.ContentRootPath); -app.MapGatewayProxy(); - app.MapPages(Path.Combine(app.Environment.WebRootPath, "dist", "index.html")); // Configure gRPC diff --git a/DysonNetwork.Drive/appsettings.json b/DysonNetwork.Drive/appsettings.json index 872ee07..d1f5c30 100644 --- a/DysonNetwork.Drive/appsettings.json +++ b/DysonNetwork.Drive/appsettings.json @@ -131,8 +131,6 @@ ], "Service": { "Name": "DysonNetwork.Drive", - "Url": "https://localhost:7092", - "ClientCert": "../Certificates/client.crt", - "ClientKey": "../Certificates/client.key" + "Url": "https://localhost:7092" } } diff --git a/DysonNetwork.Gateway/Controllers/WellKnownController.cs b/DysonNetwork.Gateway/Controllers/WellKnownController.cs index 9fddcf3..5523c8f 100644 --- a/DysonNetwork.Gateway/Controllers/WellKnownController.cs +++ b/DysonNetwork.Gateway/Controllers/WellKnownController.cs @@ -71,7 +71,7 @@ public class WellKnownController( { if (!domainMappings.TryGetValue(key, out var domain)) continue; if (domain is not null) - serviceMap[key] = "https://" + domain; + serviceMap[key] = "http://" + domain; } return Ok(serviceMap); diff --git a/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj b/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj index 7403f3c..47c67dc 100644 --- a/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj +++ b/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj @@ -17,6 +17,7 @@ + diff --git a/DysonNetwork.Gateway/Program.cs b/DysonNetwork.Gateway/Program.cs index 7c981ea..0bf2a37 100644 --- a/DysonNetwork.Gateway/Program.cs +++ b/DysonNetwork.Gateway/Program.cs @@ -4,6 +4,8 @@ using Microsoft.AspNetCore.HttpOverrides; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + builder.Host.UseContentRoot(Directory.GetCurrentDirectory()); builder.WebHost.ConfigureKestrel(options => { @@ -18,6 +20,8 @@ builder.Services.AddControllers(); var app = builder.Build(); +app.MapDefaultEndpoints(); + app.ConfigureForwardedHeaders(app.Configuration); app.UseRequestTimeouts(); diff --git a/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs index 147983f..1ac05d4 100644 --- a/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs @@ -1,6 +1,4 @@ using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using DysonNetwork.Shared.Registry; using Yarp.ReverseProxy.Configuration; using Yarp.ReverseProxy.Transforms; @@ -30,7 +28,6 @@ public static class ServiceCollectionExtensions context.AddXForwarded(action: ForwardedTransformActions.Set); }); - services.AddRegistryService(configuration, addForwarder: false); services.AddSingleton(); return services; diff --git a/DysonNetwork.Pass/DysonNetwork.Pass.csproj b/DysonNetwork.Pass/DysonNetwork.Pass.csproj index c5e9687..88f6e6b 100644 --- a/DysonNetwork.Pass/DysonNetwork.Pass.csproj +++ b/DysonNetwork.Pass/DysonNetwork.Pass.csproj @@ -49,6 +49,7 @@ + diff --git a/DysonNetwork.Pass/Email/EmailService.cs b/DysonNetwork.Pass/Email/EmailService.cs index 77b68e0..71714c3 100644 --- a/DysonNetwork.Pass/Email/EmailService.cs +++ b/DysonNetwork.Pass/Email/EmailService.cs @@ -1,5 +1,3 @@ -using dotnet_etcd; -using dotnet_etcd.interfaces; using DysonNetwork.Shared.Proto; using Microsoft.AspNetCore.Components; diff --git a/DysonNetwork.Pass/Program.cs b/DysonNetwork.Pass/Program.cs index 6a344fa..fc96c59 100644 --- a/DysonNetwork.Pass/Program.cs +++ b/DysonNetwork.Pass/Program.cs @@ -4,20 +4,16 @@ using DysonNetwork.Pass.Startup; using DysonNetwork.Shared.Http; using DysonNetwork.Shared.PageData; using DysonNetwork.Shared.Registry; -using DysonNetwork.Shared.Stream; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure Kestrel and server options builder.ConfigureAppKestrel(builder.Configuration); -// Add metrics and telemetry -builder.Services.AddAppMetrics(); - // Add application services -builder.Services.AddRegistryService(builder.Configuration); -builder.Services.AddStreamConnection(builder.Configuration); builder.Services.AddAppServices(builder.Configuration); builder.Services.AddAppRateLimiting(); builder.Services.AddAppAuthentication(); @@ -41,6 +37,8 @@ builder.Services.AddTransient(); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Run database migrations using (var scope = app.Services.CreateScope()) { @@ -51,8 +49,6 @@ using (var scope = app.Services.CreateScope()) // Configure application middleware pipeline app.ConfigureAppMiddleware(builder.Configuration, builder.Environment.ContentRootPath); -app.MapGatewayProxy(); - app.MapPages(Path.Combine(builder.Environment.WebRootPath, "dist", "index.html")); // Configure gRPC diff --git a/DysonNetwork.Pass/Startup/MetricsConfiguration.cs b/DysonNetwork.Pass/Startup/MetricsConfiguration.cs deleted file mode 100644 index 78e33cd..0000000 --- a/DysonNetwork.Pass/Startup/MetricsConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using Prometheus; -using Prometheus.SystemMetrics; - -namespace DysonNetwork.Pass.Startup; - -public static class MetricsConfiguration -{ - public static IServiceCollection AddAppMetrics(this IServiceCollection services) - { - // Prometheus - services.UseHttpClientMetrics(); - services.AddHealthChecks(); - services.AddSystemMetrics(); - services.AddPrometheusEntityFrameworkMetrics(); - services.AddPrometheusAspNetCoreMetrics(); - services.AddPrometheusHttpClientMetrics(); - - // OpenTelemetry - services.AddOpenTelemetry() - .WithTracing(tracing => - { - tracing - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddOtlpExporter(); - }) - .WithMetrics(metrics => - { - metrics - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation() - .AddOtlpExporter(); - }); - - return services; - } -} diff --git a/DysonNetwork.Pass/appsettings.json b/DysonNetwork.Pass/appsettings.json index 8b9275e..cf67169 100644 --- a/DysonNetwork.Pass/appsettings.json +++ b/DysonNetwork.Pass/appsettings.json @@ -83,9 +83,7 @@ ], "Service": { "Name": "DysonNetwork.Pass", - "Url": "https://localhost:7058", - "ClientCert": "../Certificates/client.crt", - "ClientKey": "../Certificates/client.key" + "Url": "https://localhost:7058" }, "Etcd": { "Insecure": true diff --git a/DysonNetwork.Ring/Connection/WebSocketService.cs b/DysonNetwork.Ring/Connection/WebSocketService.cs index 7454798..8c0c78d 100644 --- a/DysonNetwork.Ring/Connection/WebSocketService.cs +++ b/DysonNetwork.Ring/Connection/WebSocketService.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Net.WebSockets; -using dotnet_etcd.interfaces; using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; using Grpc.Core; @@ -11,17 +10,14 @@ public class WebSocketService { private readonly IConfiguration _configuration; private readonly ILogger _logger; - private readonly IEtcdClient _etcdClient; private readonly IDictionary _handlerMap; public WebSocketService( IEnumerable handlers, - IEtcdClient etcdClient, ILogger logger, IConfiguration configuration ) { - _etcdClient = etcdClient; _logger = logger; _configuration = configuration; _handlerMap = handlers.ToDictionary(h => h.PacketType); @@ -59,8 +55,10 @@ public class WebSocketService } catch (Exception ex) { - _logger.LogWarning(ex, "Error while closing WebSocket for {AccountId}:{DeviceId}", key.AccountId, key.DeviceId); + _logger.LogWarning(ex, "Error while closing WebSocket for {AccountId}:{DeviceId}", key.AccountId, + key.DeviceId); } + data.Cts.Cancel(); ActiveConnections.TryRemove(key, out _); } @@ -140,45 +138,24 @@ public class WebSocketService { try { - // Get the service URL from etcd for the specified endpoint - var serviceKey = $"/services/{packet.Endpoint}"; - var response = await _etcdClient.GetAsync(serviceKey); + var serviceUrl = "https://" + packet.Endpoint; - if (response.Kvs.Count > 0) + var callInvoker = GrpcClientHelper.CreateCallInvoker(serviceUrl); + var client = new RingHandlerService.RingHandlerServiceClient(callInvoker); + + try { - var serviceUrl = response.Kvs[0].Value.ToStringUtf8(); - - var clientCertPath = _configuration["Service:ClientCert"]!; - var clientKeyPath = _configuration["Service:ClientKey"]!; - var clientCertPassword = _configuration["Service:CertPassword"]; - - var callInvoker = - GrpcClientHelper.CreateCallInvoker( - serviceUrl, - clientCertPath, - clientKeyPath, - clientCertPassword - ); - var client = new RingHandlerService.RingHandlerServiceClient(callInvoker); - - try + await client.ReceiveWebSocketPacketAsync(new ReceiveWebSocketPacketRequest { - await client.ReceiveWebSocketPacketAsync(new ReceiveWebSocketPacketRequest - { - Account = currentUser, - DeviceId = deviceId, - Packet = packet.ToProtoValue() - }); - } - catch (RpcException ex) - { - _logger.LogError(ex, $"Error forwarding packet to endpoint: {packet.Endpoint}"); - } - - return; + Account = currentUser, + DeviceId = deviceId, + Packet = packet.ToProtoValue() + }); + } + catch (RpcException ex) + { + _logger.LogError(ex, $"Error forwarding packet to endpoint: {packet.Endpoint}"); } - - _logger.LogWarning($"No service registered for endpoint: {packet.Endpoint}"); } catch (Exception ex) { @@ -197,4 +174,4 @@ public class WebSocketService CancellationToken.None ); } -} +} \ No newline at end of file diff --git a/DysonNetwork.Ring/DysonNetwork.Ring.csproj b/DysonNetwork.Ring/DysonNetwork.Ring.csproj index ae8a231..dc52c8e 100644 --- a/DysonNetwork.Ring/DysonNetwork.Ring.csproj +++ b/DysonNetwork.Ring/DysonNetwork.Ring.csproj @@ -42,6 +42,7 @@ + diff --git a/DysonNetwork.Ring/Program.cs b/DysonNetwork.Ring/Program.cs index 2425a43..eda4ae7 100644 --- a/DysonNetwork.Ring/Program.cs +++ b/DysonNetwork.Ring/Program.cs @@ -3,17 +3,16 @@ using DysonNetwork.Ring.Startup; using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Http; using DysonNetwork.Shared.Registry; -using DysonNetwork.Shared.Stream; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure Kestrel and server options builder.ConfigureAppKestrel(builder.Configuration); // Add application services -builder.Services.AddRegistryService(builder.Configuration); -builder.Services.AddStreamConnection(builder.Configuration); builder.Services.AddAppServices(builder.Configuration); builder.Services.AddAppRateLimiting(); builder.Services.AddAppAuthentication(); @@ -32,6 +31,8 @@ builder.Services.AddAppScheduledJobs(); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Run database migrations using (var scope = app.Services.CreateScope()) { diff --git a/DysonNetwork.Ring/Services/PusherServiceGrpc.cs b/DysonNetwork.Ring/Services/PusherServiceGrpc.cs index bb21a56..80a3e08 100644 --- a/DysonNetwork.Ring/Services/PusherServiceGrpc.cs +++ b/DysonNetwork.Ring/Services/PusherServiceGrpc.cs @@ -2,7 +2,6 @@ using DysonNetwork.Ring.Connection; using DysonNetwork.Ring.Email; using DysonNetwork.Ring.Notification; using DysonNetwork.Shared.Proto; -using DysonNetwork.Shared.Registry; using Google.Protobuf.WellKnownTypes; using Grpc.Core; using System.Text.Json; diff --git a/DysonNetwork.Ring/Services/QueueBackgroundService.cs b/DysonNetwork.Ring/Services/QueueBackgroundService.cs index bb3b357..a2f0f1b 100644 --- a/DysonNetwork.Ring/Services/QueueBackgroundService.cs +++ b/DysonNetwork.Ring/Services/QueueBackgroundService.cs @@ -2,7 +2,6 @@ using System.Text.Json; using DysonNetwork.Ring.Email; using DysonNetwork.Ring.Notification; using DysonNetwork.Shared.Proto; -using DysonNetwork.Shared.Registry; using DysonNetwork.Shared.Stream; using Google.Protobuf; using NATS.Client.Core; diff --git a/DysonNetwork.Ring/appsettings.json b/DysonNetwork.Ring/appsettings.json index 228bc9f..d5280bb 100644 --- a/DysonNetwork.Ring/appsettings.json +++ b/DysonNetwork.Ring/appsettings.json @@ -45,9 +45,7 @@ ], "Service": { "Name": "DysonNetwork.Ring", - "Url": "https://localhost:7259", - "ClientCert": "../Certificates/client.crt", - "ClientKey": "../Certificates/client.key" + "Url": "https://localhost:7259" }, "Etcd": { "Insecure": true diff --git a/DysonNetwork.ServiceDefaults/DysonNetwork.ServiceDefaults.csproj b/DysonNetwork.ServiceDefaults/DysonNetwork.ServiceDefaults.csproj new file mode 100644 index 0000000..9f8259b --- /dev/null +++ b/DysonNetwork.ServiceDefaults/DysonNetwork.ServiceDefaults.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + + + + + diff --git a/DysonNetwork.ServiceDefaults/Extensions.cs b/DysonNetwork.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..2a1ded0 --- /dev/null +++ b/DysonNetwork.ServiceDefaults/Extensions.cs @@ -0,0 +1,132 @@ +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 .NET 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"]; + }); + + builder.AddNatsClient("queue"); + builder.AddRedisClient("cache"); + + 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) + ) + .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; + } +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Auth/Startup.cs b/DysonNetwork.Shared/Auth/Startup.cs index 3cd02ed..9197c34 100644 --- a/DysonNetwork.Shared/Auth/Startup.cs +++ b/DysonNetwork.Shared/Auth/Startup.cs @@ -1,4 +1,3 @@ -using dotnet_etcd.interfaces; using DysonNetwork.Shared.Proto; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -11,32 +10,14 @@ public static class DysonAuthStartup this IServiceCollection services ) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateAuthServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://pass"); }); - - services.AddSingleton(sp => - { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - return GrpcClientHelper - .CreatePermissionServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + services.AddGrpcClient(o => + { + o.Address = new Uri("https://pass"); }); services.AddAuthentication(options => diff --git a/DysonNetwork.Shared/DysonNetwork.Shared.csproj b/DysonNetwork.Shared/DysonNetwork.Shared.csproj index 341d5bf..a79c88a 100644 --- a/DysonNetwork.Shared/DysonNetwork.Shared.csproj +++ b/DysonNetwork.Shared/DysonNetwork.Shared.csproj @@ -7,11 +7,11 @@ - + all @@ -27,7 +27,6 @@ - @@ -40,4 +39,8 @@ + + + + diff --git a/DysonNetwork.Shared/Http/KestrelConfiguration.cs b/DysonNetwork.Shared/Http/KestrelConfiguration.cs index bd6bf1b..b64021c 100644 --- a/DysonNetwork.Shared/Http/KestrelConfiguration.cs +++ b/DysonNetwork.Shared/Http/KestrelConfiguration.cs @@ -20,20 +20,6 @@ public static class KestrelConfiguration builder.WebHost.ConfigureKestrel(options => { options.Limits.MaxRequestBodySize = maxRequestBodySize; - - var configuredUrl = Environment.GetEnvironmentVariable("ASPNETCORE_URLS"); - if (!string.IsNullOrEmpty(configuredUrl)) return; - - var certPath = configuration["Service:ClientCert"]!; - var keyPath = configuration["Service:ClientKey"]!; - - // Load PEM cert and key manually - var certificate = X509Certificate2.CreateFromPemFile(certPath, keyPath); - // Now pass the full cert - options.ListenAnyIP(5001, listenOptions => { listenOptions.UseHttps(certificate); }); - - // Optional: HTTP fallback - options.ListenAnyIP(8080); }); return builder; diff --git a/DysonNetwork.Shared/Proto/GrpcClientHelper.cs b/DysonNetwork.Shared/Proto/GrpcClientHelper.cs index 65ca1d6..55ff54e 100644 --- a/DysonNetwork.Shared/Proto/GrpcClientHelper.cs +++ b/DysonNetwork.Shared/Proto/GrpcClientHelper.cs @@ -2,171 +2,13 @@ using System.Net; using Grpc.Net.Client; using System.Security.Cryptography.X509Certificates; using Grpc.Core; -using dotnet_etcd.interfaces; namespace DysonNetwork.Shared.Proto; public static class GrpcClientHelper { - public static CallInvoker CreateCallInvoker( - string url, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) + public static CallInvoker CreateCallInvoker(string url) { - var handler = new HttpClientHandler(); - handler.ClientCertificates.Add( - clientCertPassword is null - ? X509Certificate2.CreateFromPemFile(clientCertPath, clientKeyPath) - : X509Certificate2.CreateFromEncryptedPemFile(clientCertPath, clientCertPassword, clientKeyPath) - ); - handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true; - var httpClient = new HttpClient(handler); - httpClient.DefaultRequestVersion = HttpVersion.Version20; - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - return GrpcChannel.ForAddress(url, new GrpcChannelOptions { HttpClient = httpClient }).CreateCallInvoker(); - } - - private static async Task GetServiceUrlFromEtcd(IEtcdClient etcdClient, string serviceName) - { - var response = await etcdClient.GetAsync($"/services/{serviceName}"); - return response.Kvs.Count == 0 - ? throw new InvalidOperationException($"Service '{serviceName}' not found in Etcd.") - : response.Kvs[0].Value.ToStringUtf8(); - } - - public static async Task CreateAccountServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new AccountService.AccountServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task - CreateBotAccountReceiverServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new BotAccountReceiverService.BotAccountReceiverServiceClient(CreateCallInvoker(url, clientCertPath, - clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateActionLogServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new ActionLogService.ActionLogServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateAuthServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new AuthService.AuthServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreatePermissionServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new PermissionService.PermissionServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreatePaymentServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass"); - return new PaymentService.PaymentServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateRingServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Ring"); - return new RingService.RingServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateFileServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Drive"); - return new FileService.FileServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateFileReferenceServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Drive"); - return new FileReferenceService.FileReferenceServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreatePublisherServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Sphere"); - return new PublisherService.PublisherServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); - } - - public static async Task CreateCustomAppServiceClient( - IEtcdClient etcdClient, - string clientCertPath, - string clientKeyPath, - string? clientCertPassword = null - ) - { - var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Develop"); - return new CustomAppService.CustomAppServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, - clientCertPassword)); + return GrpcChannel.ForAddress(url).CreateCallInvoker(); } } \ No newline at end of file diff --git a/DysonNetwork.Shared/Proto/pusher.proto b/DysonNetwork.Shared/Proto/ring.proto similarity index 100% rename from DysonNetwork.Shared/Proto/pusher.proto rename to DysonNetwork.Shared/Proto/ring.proto diff --git a/DysonNetwork.Shared/Registry/GatewayReverseProxy.cs b/DysonNetwork.Shared/Registry/GatewayReverseProxy.cs deleted file mode 100644 index 5e7a1c0..0000000 --- a/DysonNetwork.Shared/Registry/GatewayReverseProxy.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Diagnostics; -using System.Net; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Yarp.ReverseProxy.Forwarder; - -namespace DysonNetwork.Shared.Registry; - -public static class GatewayReverseProxy -{ - /// - /// Provides reverse proxy for DysonNetwork.Gateway. - /// Give the ability to the contained frontend to access other services via the gateway. - /// - /// The asp.net core application - /// The modified application - public static WebApplication MapGatewayProxy(this WebApplication app) - { - var httpClient = new HttpMessageInvoker(new SocketsHttpHandler - { - UseProxy = false, - AllowAutoRedirect = true, - AutomaticDecompression = DecompressionMethods.All, - UseCookies = true, - EnableMultipleHttp2Connections = true, - ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), - ConnectTimeout = TimeSpan.FromSeconds(15), - }); - - var transformer = new GatewayReverseProxyTransformer(); - var requestConfig = new ForwarderRequestConfig(); - - app.Map("/cgi/{**catch-all}", async (HttpContext context, IHttpForwarder forwarder) => - { - var registry = context.RequestServices.GetRequiredService(); - var gatewayUrl = await registry.GetServiceUrl("DysonNetwork.Gateway"); - if (gatewayUrl is null) - { - context.Response.StatusCode = 404; - await context.Response.WriteAsync("Gateway not found"); - return; - } - - var error = await forwarder.SendAsync( - context, - gatewayUrl, - httpClient, - requestConfig, - transformer - ); - if (error != ForwarderError.None) - { - var errorFeature = context.GetForwarderErrorFeature(); - var exception = errorFeature?.Exception; - context.Response.StatusCode = 502; - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync($"Gateway remote error: {exception?.Message}"); - } - }); - - return app; - } -} - -public class GatewayReverseProxyTransformer : HttpTransformer -{ - private const string Value = "/cgi"; - - public override ValueTask TransformRequestAsync( - HttpContext httpContext, - HttpRequestMessage proxyRequest, - string destinationPrefix, - CancellationToken cancellationToken - ) - { - httpContext.Request.Path = httpContext.Request.Path.StartsWithSegments(Value, out var remaining) - ? remaining - : httpContext.Request.Path; - - return Default.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); - } -} \ No newline at end of file diff --git a/DysonNetwork.Shared/Registry/RegistryHostedService.cs b/DysonNetwork.Shared/Registry/RegistryHostedService.cs deleted file mode 100644 index a1d8383..0000000 --- a/DysonNetwork.Shared/Registry/RegistryHostedService.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace DysonNetwork.Shared.Registry; - -public class RegistryHostedService( - ServiceRegistry serviceRegistry, - IConfiguration configuration, - ILogger logger -) - : IHostedService -{ - private CancellationTokenSource? _cts; - - public async Task StartAsync(CancellationToken cancellationToken) - { - var serviceName = configuration["Service:Name"]; - var serviceUrl = configuration["Service:Url"]; - var insecure = configuration.GetValue("Etcd:Insecure"); - var remote = configuration.GetConnectionString("Etcd"); - - if (insecure) - logger.LogWarning("Etcd is configured to use insecure channel."); - - if (string.IsNullOrEmpty(serviceUrl) || string.IsNullOrEmpty(serviceName)) - { - logger.LogWarning("Service URL or Service Name was not configured. Skipping Etcd registration."); - return; - } - - logger.LogInformation( - "Registering service {ServiceName} at {ServiceUrl} with Etcd ({Remote}).", - serviceName, - serviceUrl, - remote - ); - try - { - _cts = new CancellationTokenSource(); - await serviceRegistry.RegisterService(serviceName, serviceUrl, cancellationToken: _cts.Token); - logger.LogInformation("Service {ServiceName} registered successfully.", serviceName); - } - catch (Exception ex) - { - logger.LogError(ex, "Failed to register service {ServiceName} with Etcd.", serviceName); - } - } - - public async Task StopAsync(CancellationToken cancellationToken) - { - _cts?.Cancel(); - - // The lease will expire automatically if the service stops ungracefully. - var serviceName = configuration["Service:Name"]; - if (serviceName is not null) - await serviceRegistry.UnregisterService(serviceName); - logger.LogInformation("Service registration hosted service is stopping."); - } -} \ No newline at end of file diff --git a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs index bd1d997..739d434 100644 --- a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs +++ b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs @@ -1,4 +1,3 @@ -using dotnet_etcd.interfaces; using DysonNetwork.Shared.Proto; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -9,80 +8,35 @@ public static class ServiceInjectionHelper { public static IServiceCollection AddRingService(this IServiceCollection services) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateRingServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); - }); + o.Address = new Uri("https://ring"); + }); return services; } public static IServiceCollection AddAccountService(this IServiceCollection services) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateAccountServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://pass"); }); services.AddSingleton(); - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateBotAccountReceiverServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://pass"); }); - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateActionLogServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://pass"); }); - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreatePaymentServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://pass"); }); return services; @@ -90,32 +44,14 @@ public static class ServiceInjectionHelper public static IServiceCollection AddDriveService(this IServiceCollection services) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateFileServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://drive"); }); - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateFileReferenceServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://drive"); }); return services; @@ -123,18 +59,9 @@ public static class ServiceInjectionHelper public static IServiceCollection AddPublisherService(this IServiceCollection services) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreatePublisherServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://sphere"); }); return services; @@ -142,18 +69,9 @@ public static class ServiceInjectionHelper public static IServiceCollection AddDevelopService(this IServiceCollection services) { - services.AddSingleton(sp => + services.AddGrpcClient(o => { - var etcdClient = sp.GetRequiredService(); - var config = sp.GetRequiredService(); - var clientCertPath = config["Service:ClientCert"]!; - var clientKeyPath = config["Service:ClientKey"]!; - var clientCertPassword = config["Service:CertPassword"]; - - return GrpcClientHelper - .CreateCustomAppServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) - .GetAwaiter() - .GetResult(); + o.Address = new Uri("https://develop"); }); return services; diff --git a/DysonNetwork.Shared/Registry/ServiceRegistry.cs b/DysonNetwork.Shared/Registry/ServiceRegistry.cs deleted file mode 100644 index 8c2e0e4..0000000 --- a/DysonNetwork.Shared/Registry/ServiceRegistry.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Text; -using dotnet_etcd.interfaces; -using Etcdserverpb; -using Google.Protobuf; -using Microsoft.Extensions.Logging; - -namespace DysonNetwork.Shared.Registry; - -public class ServiceRegistry(IEtcdClient etcd, ILogger logger) -{ - public async Task RegisterService( - string serviceName, - string serviceUrl, - long leaseTtlSeconds = 60, - CancellationToken cancellationToken = default - ) - { - var key = $"/services/{serviceName}"; - var leaseResponse = await etcd.LeaseGrantAsync( - new LeaseGrantRequest { TTL = leaseTtlSeconds }, - cancellationToken: cancellationToken - ); - await etcd.PutAsync(new PutRequest - { - Key = ByteString.CopyFrom(key, Encoding.UTF8), - Value = ByteString.CopyFrom(serviceUrl, Encoding.UTF8), - Lease = leaseResponse.ID - }, cancellationToken: cancellationToken); - - _ = Task.Run(async () => - { - try - { - await etcd.LeaseKeepAlive(leaseResponse.ID, cancellationToken); - } - catch (Exception ex) - { - logger.LogError($"Lease keep-alive failed: {ex.Message}"); - } - }, cancellationToken); - } - - public async Task UnregisterService(string serviceName) - { - var key = $"/services/{serviceName}"; - await etcd.DeleteAsync(key); - } - - public async Task GetServiceUrl(string serviceName) - { - var key = $"/services/{serviceName}"; - var response = await etcd.GetAsync(key); - return response.Kvs.Count == 0 ? null : response.Kvs[0].Value.ToStringUtf8(); - } -} \ No newline at end of file diff --git a/DysonNetwork.Shared/Registry/Startup.cs b/DysonNetwork.Shared/Registry/Startup.cs deleted file mode 100644 index e5cdeac..0000000 --- a/DysonNetwork.Shared/Registry/Startup.cs +++ /dev/null @@ -1,28 +0,0 @@ -using dotnet_etcd.DependencyInjection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace DysonNetwork.Shared.Registry; - -public static class RegistryStartup -{ - public static IServiceCollection AddRegistryService( - this IServiceCollection services, - IConfiguration configuration, - bool addForwarder = true - ) - { - services.AddEtcdClient(options => - { - options.ConnectionString = configuration.GetConnectionString("Etcd"); - options.UseInsecureChannel = configuration.GetValue("Etcd:Insecure"); - }); - services.AddSingleton(); - services.AddHostedService(); - - if (addForwarder) - services.AddHttpForwarder(); - - return services; - } -} \ No newline at end of file diff --git a/DysonNetwork.Shared/Stream/Connector.cs b/DysonNetwork.Shared/Stream/Connector.cs deleted file mode 100644 index 66fed40..0000000 --- a/DysonNetwork.Shared/Stream/Connector.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using NATS.Client.Core; - -namespace DysonNetwork.Shared.Stream; - -public static class Connector -{ - public static IServiceCollection AddStreamConnection(this IServiceCollection services, IConfiguration configuration) - { - var connectionString = configuration.GetConnectionString("Stream"); - if (connectionString is null) - throw new ArgumentNullException(nameof(connectionString)); - services.AddSingleton(_ => new NatsConnection(new NatsOpts() - { - Url = connectionString - })); - - return services; - } -} \ No newline at end of file diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index cc01ed1..7775f38 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -55,7 +55,6 @@ - @@ -160,6 +159,7 @@ + diff --git a/DysonNetwork.Sphere/Poll/PollController.cs b/DysonNetwork.Sphere/Poll/PollController.cs index d54ea7b..46a492d 100644 --- a/DysonNetwork.Sphere/Poll/PollController.cs +++ b/DysonNetwork.Sphere/Poll/PollController.cs @@ -2,7 +2,6 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; -using DysonNetwork.Sphere.Publisher; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index 8700d83..bdec808 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -2,7 +2,6 @@ using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Http; using DysonNetwork.Shared.PageData; using DysonNetwork.Shared.Registry; -using DysonNetwork.Shared.Stream; using DysonNetwork.Sphere; using DysonNetwork.Sphere.PageData; using DysonNetwork.Sphere.Startup; @@ -11,15 +10,13 @@ using Microsoft.Extensions.FileProviders; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure Kestrel and server options builder.ConfigureAppKestrel(builder.Configuration); -// Add metrics and telemetry -builder.Services.AddAppMetrics(); - // Add application services -builder.Services.AddRegistryService(builder.Configuration); -builder.Services.AddStreamConnection(builder.Configuration); + builder.Services.AddAppServices(builder.Configuration); builder.Services.AddAppRateLimiting(); builder.Services.AddAppAuthentication(); @@ -42,6 +39,8 @@ builder.Services.AddTransient(); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Run database migrations using (var scope = app.Services.CreateScope()) { @@ -52,8 +51,6 @@ using (var scope = app.Services.CreateScope()) // Configure application middleware pipeline app.ConfigureAppMiddleware(builder.Configuration); -app.MapGatewayProxy(); - app.UseDefaultFiles(); app.UseStaticFiles(new StaticFileOptions { diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index e3bf31c..09efa48 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using NodaTime; using Google.Protobuf.WellKnownTypes; -using Microsoft.AspNetCore.Http.HttpResults; namespace DysonNetwork.Sphere.Realm; diff --git a/DysonNetwork.Sphere/Startup/MetricsConfiguration.cs b/DysonNetwork.Sphere/Startup/MetricsConfiguration.cs deleted file mode 100644 index ed427da..0000000 --- a/DysonNetwork.Sphere/Startup/MetricsConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using Prometheus; -using Prometheus.SystemMetrics; - -namespace DysonNetwork.Sphere.Startup; - -public static class MetricsConfiguration -{ - public static IServiceCollection AddAppMetrics(this IServiceCollection services) - { - // Prometheus - services.UseHttpClientMetrics(); - services.AddHealthChecks(); - services.AddSystemMetrics(); - services.AddPrometheusEntityFrameworkMetrics(); - services.AddPrometheusAspNetCoreMetrics(); - services.AddPrometheusHttpClientMetrics(); - - // OpenTelemetry - services.AddOpenTelemetry() - .WithTracing(tracing => - { - tracing - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddOtlpExporter(); - }) - .WithMetrics(metrics => - { - metrics - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation() - .AddOtlpExporter(); - }); - - return services; - } -} diff --git a/DysonNetwork.Sphere/appsettings.json b/DysonNetwork.Sphere/appsettings.json index 44f42c2..e3f0399 100644 --- a/DysonNetwork.Sphere/appsettings.json +++ b/DysonNetwork.Sphere/appsettings.json @@ -39,8 +39,6 @@ }, "Service": { "Name": "DysonNetwork.Sphere", - "Url": "https://localhost:7099", - "ClientCert": "../Certificates/client.crt", - "ClientKey": "../Certificates/client.key" + "Url": "https://localhost:7099" } } diff --git a/DysonNetwork.sln b/DysonNetwork.sln index 4a515f3..6977d7a 100644 --- a/DysonNetwork.sln +++ b/DysonNetwork.sln @@ -20,6 +20,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Gateway", "Dys EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Develop", "DysonNetwork.Develop\DysonNetwork.Develop.csproj", "{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Control", "DysonNetwork.Control\DysonNetwork.Control.csproj", "{7FFED190-51C7-4302-A8B5-96C839463458}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.ServiceDefaults", "DysonNetwork.ServiceDefaults\DysonNetwork.ServiceDefaults.csproj", "{877AAD96-C257-4305-9F1C-C9D9C9BEE615}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,5 +58,13 @@ Global {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|Any CPU.Build.0 = Release|Any CPU + {7FFED190-51C7-4302-A8B5-96C839463458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FFED190-51C7-4302-A8B5-96C839463458}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FFED190-51C7-4302-A8B5-96C839463458}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FFED190-51C7-4302-A8B5-96C839463458}.Release|Any CPU.Build.0 = Release|Any CPU + {877AAD96-C257-4305-9F1C-C9D9C9BEE615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {877AAD96-C257-4305-9F1C-C9D9C9BEE615}.Debug|Any CPU.Build.0 = Debug|Any CPU + {877AAD96-C257-4305-9F1C-C9D9C9BEE615}.Release|Any CPU.ActiveCfg = Release|Any CPU + {877AAD96-C257-4305-9F1C-C9D9C9BEE615}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 6c84793..572c9cc 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -6,6 +6,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -132,6 +133,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded From c1016e496aec9dc3b1333b327e1ec254a6345b10 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Sep 2025 01:14:43 +0800 Subject: [PATCH 02/11] :sparkles: Gateway in Aspire --- DysonNetwork.Control/AppHost.cs | 41 +++++++++++++++---- .../DysonNetwork.Control.csproj | 1 + .../Properties/launchSettings.json | 9 ---- .../Properties/launchSettings.json | 9 ---- .../Properties/launchSettings.json | 9 ---- .../Properties/launchSettings.json | 9 ---- DysonNetwork.ServiceDefaults/Extensions.cs | 14 ++++--- .../Properties/launchSettings.json | 9 ---- DysonNetwork.sln.DotSettings.user | 2 + 9 files changed, 43 insertions(+), 60 deletions(-) diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index a431e7c..36ca30f 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -1,30 +1,53 @@ +using Aspire.Hosting.Yarp.Transforms; + var builder = DistributedApplication.CreateBuilder(args); var database = builder.AddPostgres("database"); var cache = builder.AddConnectionString("cache"); var queue = builder.AddNats("queue").WithJetStream(); -var ring = builder.AddProject("ring") +var ringService = builder.AddProject("ring") .WithReference(database) .WithReference(queue); -var pass = builder.AddProject("pass") +var passService = builder.AddProject("pass") .WithReference(database) .WithReference(cache) .WithReference(queue) - .WithReference(ring); -builder.AddProject("drive") + .WithReference(ringService); +var driveService = builder.AddProject("drive") .WithReference(database) .WithReference(cache) .WithReference(queue) - .WithReference(pass); -builder.AddProject("sphere") + .WithReference(passService); +var sphereService = builder.AddProject("sphere") .WithReference(database) .WithReference(cache) .WithReference(queue) - .WithReference(pass); -builder.AddProject("develop") + .WithReference(passService); +var developService = builder.AddProject("develop") .WithReference(database) .WithReference(cache) - .WithReference(pass); + .WithReference(passService); + +var gateway = builder.AddYarp("gateway") + .WithHostPort(5000) + .WithConfiguration(yarp => + { + yarp.AddRoute("/ring/{**catch-all}", ringService) + .WithTransformPathRemovePrefix("/ring") + .WithTransformPathPrefix("/api"); + yarp.AddRoute("/id/{**catch-all}", passService) + .WithTransformPathRemovePrefix("/id") + .WithTransformPathPrefix("/api"); + yarp.AddRoute("/drive/{**catch-all}", driveService) + .WithTransformPathRemovePrefix("/drive") + .WithTransformPathPrefix("/api"); + yarp.AddRoute("/sphere/{**catch-all}", sphereService) + .WithTransformPathRemovePrefix("/sphere") + .WithTransformPathPrefix("/api"); + yarp.AddRoute("/develop/{**catch-all}", developService) + .WithTransformPathRemovePrefix("/develop") + .WithTransformPathPrefix("/api"); + }); builder.Build().Run(); \ No newline at end of file diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj index ed47623..7ca99c2 100644 --- a/DysonNetwork.Control/DysonNetwork.Control.csproj +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -15,6 +15,7 @@ + diff --git a/DysonNetwork.Develop/Properties/launchSettings.json b/DysonNetwork.Develop/Properties/launchSettings.json index 32476e3..92cb95a 100644 --- a/DysonNetwork.Develop/Properties/launchSettings.json +++ b/DysonNetwork.Develop/Properties/launchSettings.json @@ -9,15 +9,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7192;http://localhost:5156", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/DysonNetwork.Drive/Properties/launchSettings.json b/DysonNetwork.Drive/Properties/launchSettings.json index d4fe22b..3e3be25 100644 --- a/DysonNetwork.Drive/Properties/launchSettings.json +++ b/DysonNetwork.Drive/Properties/launchSettings.json @@ -9,15 +9,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7092;http://localhost:5090", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/DysonNetwork.Pass/Properties/launchSettings.json b/DysonNetwork.Pass/Properties/launchSettings.json index 23c7313..cf67092 100644 --- a/DysonNetwork.Pass/Properties/launchSettings.json +++ b/DysonNetwork.Pass/Properties/launchSettings.json @@ -9,15 +9,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7058;http://localhost:5216", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/DysonNetwork.Ring/Properties/launchSettings.json b/DysonNetwork.Ring/Properties/launchSettings.json index 982a366..c0f253c 100644 --- a/DysonNetwork.Ring/Properties/launchSettings.json +++ b/DysonNetwork.Ring/Properties/launchSettings.json @@ -9,15 +9,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7259;http://localhost:5212", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/DysonNetwork.ServiceDefaults/Extensions.cs b/DysonNetwork.ServiceDefaults/Extensions.cs index 2a1ded0..fbd1608 100644 --- a/DysonNetwork.ServiceDefaults/Extensions.cs +++ b/DysonNetwork.ServiceDefaults/Extensions.cs @@ -3,7 +3,6 @@ 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; @@ -36,13 +35,16 @@ public static class Extensions }); // Uncomment the following to restrict the allowed schemes for service discovery. - builder.Services.Configure(options => - { - options.AllowedSchemes = ["https"]; - }); + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); builder.AddNatsClient("queue"); - builder.AddRedisClient("cache"); + builder.AddRedisClient("cache", configureOptions: opts => + { + opts.AbortOnConnectFail = false; + }); return builder; } diff --git a/DysonNetwork.Sphere/Properties/launchSettings.json b/DysonNetwork.Sphere/Properties/launchSettings.json index a6ab6aa..0113940 100644 --- a/DysonNetwork.Sphere/Properties/launchSettings.json +++ b/DysonNetwork.Sphere/Properties/launchSettings.json @@ -9,15 +9,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7099;http://localhost:5071", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 572c9cc..ee7b1ce 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -35,6 +35,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -82,6 +83,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded From 8dfe201afec610fe1394cd737deacaa89ba0c3a1 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Sep 2025 01:37:17 +0800 Subject: [PATCH 03/11] :bug: Fixes bugs, endless CA issue, and endless unsecure grpc --- DysonNetwork.Control/AppHost.cs | 40 ++- .../DysonNetwork.Control.csproj | 1 - .../Properties/launchSettings.json | 9 + .../Properties/launchSettings.json | 9 + .../Controllers/WellKnownController.cs | 108 -------- DysonNetwork.Gateway/Dockerfile | 23 -- .../DysonNetwork.Gateway.csproj | 24 -- DysonNetwork.Gateway/Program.cs | 40 --- .../Properties/launchSettings.json | 23 -- .../RegistryProxyConfigProvider.cs | 239 ------------------ .../Startup/ServiceCollectionExtensions.cs | 35 --- DysonNetwork.Gateway/VersionController.cs | 20 -- DysonNetwork.Gateway/appsettings.json | 49 ---- DysonNetwork.Gateway/version.json | 7 - .../Properties/launchSettings.json | 9 + .../Properties/launchSettings.json | 9 + DysonNetwork.ServiceDefaults/Extensions.cs | 3 + .../Http/KestrelConfiguration.cs | 2 +- .../Properties/launchSettings.json | 9 + DysonNetwork.sln | 6 - DysonNetwork.sln.DotSettings.user | 1 + 21 files changed, 76 insertions(+), 590 deletions(-) delete mode 100644 DysonNetwork.Gateway/Controllers/WellKnownController.cs delete mode 100644 DysonNetwork.Gateway/Dockerfile delete mode 100644 DysonNetwork.Gateway/DysonNetwork.Gateway.csproj delete mode 100644 DysonNetwork.Gateway/Program.cs delete mode 100644 DysonNetwork.Gateway/Properties/launchSettings.json delete mode 100644 DysonNetwork.Gateway/RegistryProxyConfigProvider.cs delete mode 100644 DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs delete mode 100644 DysonNetwork.Gateway/VersionController.cs delete mode 100644 DysonNetwork.Gateway/appsettings.json delete mode 100644 DysonNetwork.Gateway/version.json diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index 36ca30f..ecc737f 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -2,50 +2,62 @@ using Aspire.Hosting.Yarp.Transforms; var builder = DistributedApplication.CreateBuilder(args); -var database = builder.AddPostgres("database"); +// Database was configured separately in each service. +// var database = builder.AddPostgres("database"); + var cache = builder.AddConnectionString("cache"); var queue = builder.AddNats("queue").WithJetStream(); var ringService = builder.AddProject("ring") - .WithReference(database) .WithReference(queue); var passService = builder.AddProject("pass") - .WithReference(database) .WithReference(cache) .WithReference(queue) .WithReference(ringService); var driveService = builder.AddProject("drive") - .WithReference(database) .WithReference(cache) .WithReference(queue) - .WithReference(passService); + .WithReference(passService) + .WithReference(ringService); var sphereService = builder.AddProject("sphere") - .WithReference(database) .WithReference(cache) .WithReference(queue) - .WithReference(passService); + .WithReference(passService) + .WithReference(ringService); var developService = builder.AddProject("develop") - .WithReference(database) .WithReference(cache) - .WithReference(passService); + .WithReference(passService) + .WithReference(ringService); + +// Extra double-ended references +ringService.WithReference(passService); var gateway = builder.AddYarp("gateway") .WithHostPort(5000) .WithConfiguration(yarp => { - yarp.AddRoute("/ring/{**catch-all}", ringService) + var ringCluster = yarp.AddCluster(ringService.GetEndpoint("http")); + yarp.AddRoute("/ws", ringCluster); + yarp.AddRoute("/ring/{**catch-all}", ringCluster) .WithTransformPathRemovePrefix("/ring") .WithTransformPathPrefix("/api"); - yarp.AddRoute("/id/{**catch-all}", passService) + var passCluster = yarp.AddCluster(passService.GetEndpoint("http")); + yarp.AddRoute("/.well-known/openid-configuration", passCluster); + yarp.AddRoute("/.well-known/jwks", passCluster); + yarp.AddRoute("/id/{**catch-all}", passCluster) .WithTransformPathRemovePrefix("/id") .WithTransformPathPrefix("/api"); - yarp.AddRoute("/drive/{**catch-all}", driveService) + var driveCluster = yarp.AddCluster(driveService.GetEndpoint("http")); + yarp.AddRoute("/api/tus", driveCluster); + yarp.AddRoute("/drive/{**catch-all}", driveCluster) .WithTransformPathRemovePrefix("/drive") .WithTransformPathPrefix("/api"); - yarp.AddRoute("/sphere/{**catch-all}", sphereService) + var sphereCluster = yarp.AddCluster(sphereService.GetEndpoint("http")); + yarp.AddRoute("/sphere/{**catch-all}", sphereCluster) .WithTransformPathRemovePrefix("/sphere") .WithTransformPathPrefix("/api"); - yarp.AddRoute("/develop/{**catch-all}", developService) + var developCluster = yarp.AddCluster(developService.GetEndpoint("http")); + yarp.AddRoute("/develop/{**catch-all}", developCluster) .WithTransformPathRemovePrefix("/develop") .WithTransformPathPrefix("/api"); }); diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj index 7ca99c2..20416d3 100644 --- a/DysonNetwork.Control/DysonNetwork.Control.csproj +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -21,7 +21,6 @@ - diff --git a/DysonNetwork.Develop/Properties/launchSettings.json b/DysonNetwork.Develop/Properties/launchSettings.json index 92cb95a..32476e3 100644 --- a/DysonNetwork.Develop/Properties/launchSettings.json +++ b/DysonNetwork.Develop/Properties/launchSettings.json @@ -9,6 +9,15 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7192;http://localhost:5156", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/DysonNetwork.Drive/Properties/launchSettings.json b/DysonNetwork.Drive/Properties/launchSettings.json index 3e3be25..d4fe22b 100644 --- a/DysonNetwork.Drive/Properties/launchSettings.json +++ b/DysonNetwork.Drive/Properties/launchSettings.json @@ -9,6 +9,15 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7092;http://localhost:5090", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/DysonNetwork.Gateway/Controllers/WellKnownController.cs b/DysonNetwork.Gateway/Controllers/WellKnownController.cs deleted file mode 100644 index 5523c8f..0000000 --- a/DysonNetwork.Gateway/Controllers/WellKnownController.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Text; -using System.Text.Json.Serialization; -using dotnet_etcd.interfaces; -using Microsoft.AspNetCore.Mvc; -using Yarp.ReverseProxy.Configuration; - -namespace DysonNetwork.Gateway.Controllers; - -[ApiController] -[Route("/.well-known")] -public class WellKnownController( - IConfiguration configuration, - IProxyConfigProvider proxyConfigProvider, - IEtcdClient etcdClient) - : ControllerBase -{ - public class IpCheckResponse - { - [JsonPropertyName("remote_ip")] public string? RemoteIp { get; set; } - [JsonPropertyName("x_forwarded_for")] public string? XForwardedFor { get; set; } - [JsonPropertyName("x_forwarded_proto")] public string? XForwardedProto { get; set; } - [JsonPropertyName("x_forwarded_host")] public string? XForwardedHost { get; set; } - [JsonPropertyName("x_real_ip")] public string? XRealIp { get; set; } - } - - [HttpGet("ip-check")] - public ActionResult GetIpCheck() - { - var ip = HttpContext.Connection.RemoteIpAddress?.ToString(); - - var xForwardedFor = Request.Headers["X-Forwarded-For"].FirstOrDefault(); - var xForwardedProto = Request.Headers["X-Forwarded-Proto"].FirstOrDefault(); - var xForwardedHost = Request.Headers["X-Forwarded-Host"].FirstOrDefault(); - var realIp = Request.Headers["X-Real-IP"].FirstOrDefault(); - - return Ok(new IpCheckResponse - { - RemoteIp = ip, - XForwardedFor = xForwardedFor, - XForwardedProto = xForwardedProto, - XForwardedHost = xForwardedHost, - XRealIp = realIp - }); - } - - [HttpGet("domains")] - public IActionResult GetDomainMappings() - { - var domainMappings = configuration.GetSection("DomainMappings").GetChildren() - .ToDictionary(x => x.Key, x => x.Value); - return Ok(domainMappings); - } - - [HttpGet("services")] - public IActionResult GetServices() - { - var local = configuration.GetValue("LocalMode"); - var response = etcdClient.GetRange("/services/"); - var kvs = response.Kvs; - - var serviceMap = kvs.ToDictionary( - kv => Encoding.UTF8.GetString(kv.Key.ToByteArray()).Replace("/services/", ""), - kv => Encoding.UTF8.GetString(kv.Value.ToByteArray()) - ); - - if (local) return Ok(serviceMap); - - var domainMappings = configuration.GetSection("DomainMappings").GetChildren() - .ToDictionary(x => x.Key, x => x.Value); - foreach (var (key, _) in serviceMap.ToList()) - { - if (!domainMappings.TryGetValue(key, out var domain)) continue; - if (domain is not null) - serviceMap[key] = "http://" + domain; - } - - return Ok(serviceMap); - } - - [HttpGet("routes")] - public IActionResult GetProxyRules() - { - var config = proxyConfigProvider.GetConfig(); - var rules = config.Routes.Select(r => new - { - r.RouteId, - r.ClusterId, - Match = new - { - r.Match.Path, - Hosts = r.Match.Hosts != null ? string.Join(", ", r.Match.Hosts) : null - }, - Transforms = r.Transforms?.Select(t => t.Select(kv => $"{kv.Key}: {kv.Value}").ToList()) - }).ToList(); - - var clusters = config.Clusters.Select(c => new - { - c.ClusterId, - Destinations = c.Destinations?.Select(d => new - { - d.Key, - d.Value.Address - }).ToList() - }).ToList(); - - return Ok(new { Rules = rules, Clusters = clusters }); - } -} \ No newline at end of file diff --git a/DysonNetwork.Gateway/Dockerfile b/DysonNetwork.Gateway/Dockerfile deleted file mode 100644 index b9273da..0000000 --- a/DysonNetwork.Gateway/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base -USER $APP_UID -WORKDIR /app -EXPOSE 8080 -EXPOSE 8081 - -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build -ARG BUILD_CONFIGURATION=Release -WORKDIR /src -COPY ["DysonNetwork.Gateway/DysonNetwork.Gateway.csproj", "DysonNetwork.Gateway/"] -RUN dotnet restore "DysonNetwork.Gateway/DysonNetwork.Gateway.csproj" -COPY . . -WORKDIR "/src/DysonNetwork.Gateway" -RUN dotnet build "./DysonNetwork.Gateway.csproj" -c $BUILD_CONFIGURATION -o /app/build - -FROM build AS publish -ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "./DysonNetwork.Gateway.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "DysonNetwork.Gateway.dll"] \ No newline at end of file diff --git a/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj b/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj deleted file mode 100644 index 47c67dc..0000000 --- a/DysonNetwork.Gateway/DysonNetwork.Gateway.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net9.0 - enable - enable - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - diff --git a/DysonNetwork.Gateway/Program.cs b/DysonNetwork.Gateway/Program.cs deleted file mode 100644 index 0bf2a37..0000000 --- a/DysonNetwork.Gateway/Program.cs +++ /dev/null @@ -1,40 +0,0 @@ -using DysonNetwork.Gateway.Startup; -using DysonNetwork.Shared.Http; -using Microsoft.AspNetCore.HttpOverrides; - -var builder = WebApplication.CreateBuilder(args); - -builder.AddServiceDefaults(); - -builder.Host.UseContentRoot(Directory.GetCurrentDirectory()); -builder.WebHost.ConfigureKestrel(options => -{ - options.Limits.MaxRequestBodySize = long.MaxValue; - options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2); - options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30); -}); - -// Add services to the container. -builder.Services.AddGateway(builder.Configuration); -builder.Services.AddControllers(); - -var app = builder.Build(); - -app.MapDefaultEndpoints(); - -app.ConfigureForwardedHeaders(app.Configuration); - -app.UseRequestTimeouts(); -app.UseCors(opts => - opts.SetIsOriginAllowed(_ => true) - .WithExposedHeaders("*") - .WithHeaders("*") - .AllowCredentials() - .AllowAnyHeader() - .AllowAnyMethod() -); - -app.MapControllers(); -app.MapReverseProxy(); - -app.Run(); diff --git a/DysonNetwork.Gateway/Properties/launchSettings.json b/DysonNetwork.Gateway/Properties/launchSettings.json deleted file mode 100644 index 4551407..0000000 --- a/DysonNetwork.Gateway/Properties/launchSettings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "http://localhost:5094", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7034;http://0.0.0.0:5094", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/DysonNetwork.Gateway/RegistryProxyConfigProvider.cs b/DysonNetwork.Gateway/RegistryProxyConfigProvider.cs deleted file mode 100644 index 4c4bc91..0000000 --- a/DysonNetwork.Gateway/RegistryProxyConfigProvider.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System.Text; -using dotnet_etcd.interfaces; -using Yarp.ReverseProxy.Configuration; -using Yarp.ReverseProxy.Forwarder; - -namespace DysonNetwork.Gateway; - -public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable -{ - private readonly object _lock = new(); - private readonly IEtcdClient _etcdClient; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; - private readonly CancellationTokenSource _watchCts = new(); - private CancellationTokenSource _cts; - private IProxyConfig _config; - - public RegistryProxyConfigProvider( - IEtcdClient etcdClient, - IConfiguration configuration, - ILogger logger - ) - { - _etcdClient = etcdClient; - _configuration = configuration; - _logger = logger; - _cts = new CancellationTokenSource(); - _config = LoadConfig(); - - // Watch for changes in etcd - _etcdClient.WatchRange("/services/", _ => - { - _logger.LogInformation("Etcd configuration changed. Reloading proxy config."); - ReloadConfig(); - }, cancellationToken: _watchCts.Token); - } - - public IProxyConfig GetConfig() => _config; - - private void ReloadConfig() - { - lock (_lock) - { - var oldCts = _cts; - _cts = new CancellationTokenSource(); - _config = LoadConfig(); - oldCts.Cancel(); - oldCts.Dispose(); - } - } - - private IProxyConfig LoadConfig() - { - _logger.LogInformation("Generating new proxy config."); - var response = _etcdClient.GetRange("/services/"); - var kvs = response.Kvs; - - var serviceMap = kvs.ToDictionary( - kv => Encoding.UTF8.GetString(kv.Key.ToByteArray()).Replace("/services/", ""), - kv => Encoding.UTF8.GetString(kv.Value.ToByteArray()) - ); - - var clusters = new List(); - var routes = new List(); - - var domainMappings = _configuration.GetSection("DomainMappings").GetChildren() - .ToDictionary(x => x.Key, x => x.Value); - - var pathAliases = _configuration.GetSection("PathAliases").GetChildren() - .ToDictionary(x => x.Key, x => x.Value); - - var directRoutes = _configuration.GetSection("DirectRoutes").Get>() ?? - []; - - _logger.LogInformation("Indexing {ServiceCount} services from Etcd.", kvs.Count); - - var gatewayServiceName = _configuration["Service:Name"]; - - // Add direct routes - foreach (var directRoute in directRoutes) - { - if (serviceMap.TryGetValue(directRoute.Service, out var serviceUrl)) - { - var existingCluster = clusters.FirstOrDefault(c => c.ClusterId == directRoute.Service); - if (existingCluster is null) - { - var cluster = new ClusterConfig - { - ClusterId = directRoute.Service, - Destinations = new Dictionary - { - { "destination1", new DestinationConfig { Address = serviceUrl } } - }, - }; - clusters.Add(cluster); - } - - var route = new RouteConfig - { - RouteId = $"direct-{directRoute.Service}-{directRoute.Path.Replace("/", "-")}", - ClusterId = directRoute.Service, - Match = new RouteMatch { Path = directRoute.Path }, - }; - routes.Add(route); - _logger.LogInformation(" Added Direct Route: {Path} -> {Service}", directRoute.Path, - directRoute.Service); - } - else - { - _logger.LogWarning(" Direct route service {Service} not found in Etcd.", directRoute.Service); - } - } - - foreach (var serviceName in serviceMap.Keys) - { - if (serviceName == gatewayServiceName) - { - _logger.LogInformation("Skipping gateway service: {ServiceName}", serviceName); - continue; - } - - var serviceUrl = serviceMap[serviceName]; - - // Determine the path alias - string? pathAlias; - pathAlias = pathAliases.TryGetValue(serviceName, out var alias) - ? alias - : serviceName.Split('.').Last().ToLowerInvariant(); - - _logger.LogInformation(" Service: {ServiceName}, URL: {ServiceUrl}, Path Alias: {PathAlias}", serviceName, - serviceUrl, pathAlias); - - // Check if the cluster already exists - var existingCluster = clusters.FirstOrDefault(c => c.ClusterId == serviceName); - if (existingCluster == null) - { - var cluster = new ClusterConfig - { - ClusterId = serviceName, - Destinations = new Dictionary - { - { "destination1", new DestinationConfig { Address = serviceUrl } } - } - }; - clusters.Add(cluster); - _logger.LogInformation(" Added Cluster: {ServiceName}", serviceName); - } - else if (existingCluster.Destinations is not null) - { - // Create a new cluster with merged destinations - var newDestinations = new Dictionary(existingCluster.Destinations) - { - { - $"destination{existingCluster.Destinations.Count + 1}", - new DestinationConfig { Address = serviceUrl } - } - }; - - var mergedCluster = new ClusterConfig - { - ClusterId = serviceName, - Destinations = newDestinations - }; - - // Replace the existing cluster with the merged one - var index = clusters.IndexOf(existingCluster); - clusters[index] = mergedCluster; - - _logger.LogInformation(" Updated Cluster {ServiceName} with {DestinationCount} destinations", - serviceName, mergedCluster.Destinations.Count); - } - - // Host-based routing - if (domainMappings.TryGetValue(serviceName, out var domain) && domain is not null) - { - var hostRoute = new RouteConfig - { - RouteId = $"{serviceName}-host", - ClusterId = serviceName, - Match = new RouteMatch - { - Hosts = [domain], - Path = "/{**catch-all}" - }, - }; - routes.Add(hostRoute); - _logger.LogInformation(" Added Host-based Route: {Host}", domain); - } - - // Path-based routing - var pathRoute = new RouteConfig - { - RouteId = $"{serviceName}-path", - ClusterId = serviceName, - Match = new RouteMatch { Path = $"/{pathAlias}/{{**catch-all}}" }, - Transforms = new List> - { - new() { { "PathRemovePrefix", $"/{pathAlias}" } }, - new() { { "PathPrefix", "/api" } }, - }, - Timeout = TimeSpan.FromSeconds(5) - }; - routes.Add(pathRoute); - _logger.LogInformation(" Added Path-based Route: {Path}", pathRoute.Match.Path); - } - - return new CustomProxyConfig( - routes, - clusters, - new Microsoft.Extensions.Primitives.CancellationChangeToken(_cts.Token) - ); - } - - private class CustomProxyConfig( - IReadOnlyList routes, - IReadOnlyList clusters, - Microsoft.Extensions.Primitives.IChangeToken changeToken - ) - : IProxyConfig - { - public IReadOnlyList Routes { get; } = routes; - public IReadOnlyList Clusters { get; } = clusters; - public Microsoft.Extensions.Primitives.IChangeToken ChangeToken { get; } = changeToken; - } - - public record DirectRouteConfig - { - public required string Path { get; set; } - public required string Service { get; set; } - } - - public virtual void Dispose() - { - _cts.Cancel(); - _cts.Dispose(); - _watchCts.Cancel(); - _watchCts.Dispose(); - } -} \ No newline at end of file diff --git a/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs deleted file mode 100644 index 1ac05d4..0000000 --- a/DysonNetwork.Gateway/Startup/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Net.Security; -using Yarp.ReverseProxy.Configuration; -using Yarp.ReverseProxy.Transforms; - -namespace DysonNetwork.Gateway.Startup; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddGateway(this IServiceCollection services, IConfiguration configuration) - { - services.AddRequestTimeouts(); - - services - .AddReverseProxy() - .ConfigureHttpClient((context, handler) => - { - // var caCert = X509CertificateLoader.LoadCertificateFromFile(configuration["CaCert"]!); - handler.SslOptions = new SslClientAuthenticationOptions - { - RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true - }; - }) - .AddTransforms(context => - { - context.CopyRequestHeaders = true; - context.AddOriginalHost(); - context.AddForwarded(action: ForwardedTransformActions.Set); - context.AddXForwarded(action: ForwardedTransformActions.Set); - }); - - services.AddSingleton(); - - return services; - } -} diff --git a/DysonNetwork.Gateway/VersionController.cs b/DysonNetwork.Gateway/VersionController.cs deleted file mode 100644 index 6040eae..0000000 --- a/DysonNetwork.Gateway/VersionController.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DysonNetwork.Shared.Data; -using Microsoft.AspNetCore.Mvc; - -namespace DysonNetwork.Gateway; - -[ApiController] -[Route("/api/version")] -public class VersionController : ControllerBase -{ - [HttpGet] - public IActionResult Get() - { - return Ok(new AppVersion - { - Version = ThisAssembly.AssemblyVersion, - Commit = ThisAssembly.GitCommitId, - UpdateDate = ThisAssembly.GitCommitDate - }); - } -} diff --git a/DysonNetwork.Gateway/appsettings.json b/DysonNetwork.Gateway/appsettings.json deleted file mode 100644 index 3357c9a..0000000 --- a/DysonNetwork.Gateway/appsettings.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "LocalMode": true, - "CaCert": "../Certificates/ca.crt", - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "Etcd": "etcd.orb.local:2379" - }, - "Etcd": { - "Insecure": true - }, - "Service": { - "Name": "DysonNetwork.Gateway", - "Url": "https://localhost:7034" - }, - "DomainMappings": { - "DysonNetwork.Pass": "id.solsynth.dev", - "DysonNetwork.Drive": "drive.solsynth.dev", - "DysonNetwork.Ring": "push.solsynth.dev", - "DysonNetwork.Sphere": "sphere.solsynth.dev" - }, - "PathAliases": { - "DysonNetwork.Pass": "id", - "DysonNetwork.Drive": "drive" - }, - "DirectRoutes": [ - { - "Path": "/ws", - "Service": "DysonNetwork.Ring" - }, - { - "Path": "/api/tus", - "Service": "DysonNetwork.Drive" - }, - { - "Path": "/.well-known/openid-configuration", - "Service": "DysonNetwork.Pass" - }, - { - "Path": "/.well-known/jwks", - "Service": "DysonNetwork.Pass" - } - ] -} diff --git a/DysonNetwork.Gateway/version.json b/DysonNetwork.Gateway/version.json deleted file mode 100644 index 9fbf8d3..0000000 --- a/DysonNetwork.Gateway/version.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "1.0", - "publicReleaseRefSpec": ["^refs/heads/main$"], - "cloudBuild": { - "setVersionVariables": true - } -} diff --git a/DysonNetwork.Pass/Properties/launchSettings.json b/DysonNetwork.Pass/Properties/launchSettings.json index cf67092..23c7313 100644 --- a/DysonNetwork.Pass/Properties/launchSettings.json +++ b/DysonNetwork.Pass/Properties/launchSettings.json @@ -9,6 +9,15 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7058;http://localhost:5216", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/DysonNetwork.Ring/Properties/launchSettings.json b/DysonNetwork.Ring/Properties/launchSettings.json index c0f253c..982a366 100644 --- a/DysonNetwork.Ring/Properties/launchSettings.json +++ b/DysonNetwork.Ring/Properties/launchSettings.json @@ -9,6 +9,15 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7259;http://localhost:5212", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/DysonNetwork.ServiceDefaults/Extensions.cs b/DysonNetwork.ServiceDefaults/Extensions.cs index fbd1608..4b8dc80 100644 --- a/DysonNetwork.ServiceDefaults/Extensions.cs +++ b/DysonNetwork.ServiceDefaults/Extensions.cs @@ -19,6 +19,9 @@ public static class Extensions public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder { + // Allow unencrypted grpc + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + builder.ConfigureOpenTelemetry(); builder.AddDefaultHealthChecks(); diff --git a/DysonNetwork.Shared/Http/KestrelConfiguration.cs b/DysonNetwork.Shared/Http/KestrelConfiguration.cs index b64021c..e3f0f38 100644 --- a/DysonNetwork.Shared/Http/KestrelConfiguration.cs +++ b/DysonNetwork.Shared/Http/KestrelConfiguration.cs @@ -12,7 +12,7 @@ public static class KestrelConfiguration { public static WebApplicationBuilder ConfigureAppKestrel( this WebApplicationBuilder builder, - IConfiguration configuration, + IConfiguration configuration, long maxRequestBodySize = 50 * 1024 * 1024 ) { diff --git a/DysonNetwork.Sphere/Properties/launchSettings.json b/DysonNetwork.Sphere/Properties/launchSettings.json index 0113940..a6ab6aa 100644 --- a/DysonNetwork.Sphere/Properties/launchSettings.json +++ b/DysonNetwork.Sphere/Properties/launchSettings.json @@ -9,6 +9,15 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7099;http://localhost:5071", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/DysonNetwork.sln b/DysonNetwork.sln index 6977d7a..30ddd02 100644 --- a/DysonNetwork.sln +++ b/DysonNetwork.sln @@ -16,8 +16,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Ring", "DysonN EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Drive", "DysonNetwork.Drive\DysonNetwork.Drive.csproj", "{8DE0B783-8852-494D-B90A-201ABBB71202}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Gateway", "DysonNetwork.Gateway\DysonNetwork.Gateway.csproj", "{19EB0086-4049-4B78-91C4-EAC37130A006}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Develop", "DysonNetwork.Develop\DysonNetwork.Develop.csproj", "{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Control", "DysonNetwork.Control\DysonNetwork.Control.csproj", "{7FFED190-51C7-4302-A8B5-96C839463458}" @@ -50,10 +48,6 @@ Global {8DE0B783-8852-494D-B90A-201ABBB71202}.Debug|Any CPU.Build.0 = Debug|Any CPU {8DE0B783-8852-494D-B90A-201ABBB71202}.Release|Any CPU.ActiveCfg = Release|Any CPU {8DE0B783-8852-494D-B90A-201ABBB71202}.Release|Any CPU.Build.0 = Release|Any CPU - {19EB0086-4049-4B78-91C4-EAC37130A006}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {19EB0086-4049-4B78-91C4-EAC37130A006}.Debug|Any CPU.Build.0 = Debug|Any CPU - {19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.ActiveCfg = Release|Any CPU - {19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.Build.0 = Release|Any CPU {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index ee7b1ce..2998db0 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -93,6 +93,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded From 1128c9a0ba2e98f5f058b1edbe632245ec723e68 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Sep 2025 01:39:42 +0800 Subject: [PATCH 04/11] :wastebasket: Remove useless connection strings --- DysonNetwork.Develop/appsettings.json | 5 +---- DysonNetwork.Drive/appsettings.json | 5 +---- DysonNetwork.Pass/appsettings.json | 5 +---- DysonNetwork.Ring/appsettings.json | 5 +---- DysonNetwork.Sphere/appsettings.json | 5 +---- 5 files changed, 5 insertions(+), 20 deletions(-) diff --git a/DysonNetwork.Develop/appsettings.json b/DysonNetwork.Develop/appsettings.json index ba758b3..b81e2d8 100644 --- a/DysonNetwork.Develop/appsettings.json +++ b/DysonNetwork.Develop/appsettings.json @@ -10,10 +10,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60", - "FastRetrieve": "localhost:6379", - "Etcd": "etcd.orb.local:2379", - "Stream": "nats.orb.local:4222" + "App": "Host=localhost;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "KnownProxies": [ "127.0.0.1", diff --git a/DysonNetwork.Drive/appsettings.json b/DysonNetwork.Drive/appsettings.json index d1f5c30..f025412 100644 --- a/DysonNetwork.Drive/appsettings.json +++ b/DysonNetwork.Drive/appsettings.json @@ -10,10 +10,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60", - "FastRetrieve": "localhost:6379", - "Etcd": "etcd.orb.local:2379", - "Stream": "nats.orb.local:4222" + "App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Authentication": { "Schemes": { diff --git a/DysonNetwork.Pass/appsettings.json b/DysonNetwork.Pass/appsettings.json index cf67169..066d1a3 100644 --- a/DysonNetwork.Pass/appsettings.json +++ b/DysonNetwork.Pass/appsettings.json @@ -10,10 +10,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60", - "FastRetrieve": "localhost:6379", - "Etcd": "etcd.orb.local:2379", - "Stream": "nats.orb.local:4222" + "App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Authentication": { "Schemes": { diff --git a/DysonNetwork.Ring/appsettings.json b/DysonNetwork.Ring/appsettings.json index d5280bb..2ab4e9e 100644 --- a/DysonNetwork.Ring/appsettings.json +++ b/DysonNetwork.Ring/appsettings.json @@ -9,10 +9,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60", - "FastRetrieve": "localhost:6379", - "Etcd": "etcd.orb.local:2379", - "Stream": "nats.orb.local:4222" + "App": "Host=localhost;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Notifications": { "Push": { diff --git a/DysonNetwork.Sphere/appsettings.json b/DysonNetwork.Sphere/appsettings.json index e3f0399..1157d91 100644 --- a/DysonNetwork.Sphere/appsettings.json +++ b/DysonNetwork.Sphere/appsettings.json @@ -10,10 +10,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60", - "FastRetrieve": "localhost:6379", - "Etcd": "etcd.orb.local:2379", - "Stream": "nats.orb.local:4222" + "App": "Host=localhost;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "GeoIp": { "DatabasePath": "./Keys/GeoLite2-City.mmdb" From bd8e13f25d488618c6a436cb0df1a6b292035d47 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 15 Sep 2025 01:44:18 +0800 Subject: [PATCH 05/11] :recycle: Replace use aspire redis --- DysonNetwork.Control/AppHost.cs | 2 +- DysonNetwork.Control/DysonNetwork.Control.csproj | 2 +- DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs | 5 ----- DysonNetwork.Drive/Startup/ServiceCollectionExtensions.cs | 5 ----- DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs | 5 ----- DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs | 5 ----- DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs | 5 ----- DysonNetwork.sln.DotSettings.user | 1 + 8 files changed, 3 insertions(+), 27 deletions(-) diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index ecc737f..7a390e7 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -5,7 +5,7 @@ var builder = DistributedApplication.CreateBuilder(args); // Database was configured separately in each service. // var database = builder.AddPostgres("database"); -var cache = builder.AddConnectionString("cache"); +var cache = builder.AddRedis("cache"); var queue = builder.AddNats("queue").WithJetStream(); var ringService = builder.AddProject("ring") diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj index 20416d3..822c7e5 100644 --- a/DysonNetwork.Control/DysonNetwork.Control.csproj +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -14,7 +14,7 @@ - + diff --git a/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs index 216ac2e..0ce5715 100644 --- a/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs @@ -20,11 +20,6 @@ public static class ServiceCollectionExtensions services.AddDbContext(); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); - services.AddSingleton(_ => - { - var connection = configuration.GetConnectionString("FastRetrieve")!; - return ConnectionMultiplexer.Connect(connection); - }); services.AddSingleton(); services.AddHttpClient(); diff --git a/DysonNetwork.Drive/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Drive/Startup/ServiceCollectionExtensions.cs index 05f220e..66e7369 100644 --- a/DysonNetwork.Drive/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Drive/Startup/ServiceCollectionExtensions.cs @@ -17,11 +17,6 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddAppServices(this IServiceCollection services, IConfiguration configuration) { services.AddDbContext(); // Assuming you'll have an AppDatabase - services.AddSingleton(_ => - { - var connection = configuration.GetConnectionString("FastRetrieve")!; - return ConnectionMultiplexer.Connect(connection); - }); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); services.AddSingleton(); // Uncomment if you have CacheServiceRedis diff --git a/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs index 858b568..cda0085 100644 --- a/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs @@ -34,11 +34,6 @@ public static class ServiceCollectionExtensions services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddDbContext(); - services.AddSingleton(_ => - { - var connection = configuration.GetConnectionString("FastRetrieve")!; - return ConnectionMultiplexer.Connect(connection); - }); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); services.AddSingleton(); diff --git a/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs index 63677b4..0480fcd 100644 --- a/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs @@ -21,11 +21,6 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddAppServices(this IServiceCollection services, IConfiguration configuration) { services.AddDbContext(); - services.AddSingleton(_ => - { - var connection = configuration.GetConnectionString("FastRetrieve")!; - return ConnectionMultiplexer.Connect(connection); - }); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); services.AddSingleton(); diff --git a/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs index 69ad0bc..7077a81 100644 --- a/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs @@ -31,11 +31,6 @@ public static class ServiceCollectionExtensions services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddDbContext(); - services.AddSingleton(_ => - { - var connection = configuration.GetConnectionString("FastRetrieve")!; - return ConnectionMultiplexer.Connect(connection); - }); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); services.AddSingleton(); diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 2998db0..383843d 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -66,6 +66,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded From 49beb17925655302b876e239c1de72d21ba39a24 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 16 Sep 2025 00:47:18 +0800 Subject: [PATCH 06/11] :bricks: Make .NET Aspire uses docker compose --- .aspire/settings.json | 3 +++ DysonNetwork.Control/AppHost.cs | 4 +++- DysonNetwork.Control/DysonNetwork.Control.csproj | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .aspire/settings.json diff --git a/.aspire/settings.json b/.aspire/settings.json new file mode 100644 index 0000000..9929d96 --- /dev/null +++ b/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../DysonNetwork.Control/DysonNetwork.Control.csproj" +} \ No newline at end of file diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index 7a390e7..8c18814 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -32,7 +32,7 @@ var developService = builder.AddProject("develop" // Extra double-ended references ringService.WithReference(passService); -var gateway = builder.AddYarp("gateway") +builder.AddYarp("gateway") .WithHostPort(5000) .WithConfiguration(yarp => { @@ -62,4 +62,6 @@ var gateway = builder.AddYarp("gateway") .WithTransformPathPrefix("/api"); }); +builder.AddDockerComposeEnvironment("docker-compose"); + builder.Build().Run(); \ No newline at end of file diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj index 822c7e5..f72833a 100644 --- a/DysonNetwork.Control/DysonNetwork.Control.csproj +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -13,6 +13,7 @@ + From 3caa79b9a77d793fea439b62db30b46d39a56e4f Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 16 Sep 2025 00:52:37 +0800 Subject: [PATCH 07/11] :recycle: Remove the Sphere project depends on the Pass project. Move to the shared project instead. --- DysonNetwork.Sphere/Chat/ChatRoom.cs | 6 ++---- DysonNetwork.Sphere/Chat/ChatRoomService.cs | 2 +- DysonNetwork.Sphere/DysonNetwork.Sphere.csproj | 1 - DysonNetwork.Sphere/Poll/Poll.cs | 3 +-- DysonNetwork.Sphere/Poll/PollController.cs | 3 ++- DysonNetwork.Sphere/Publisher/Publisher.cs | 2 +- DysonNetwork.Sphere/Publisher/PublisherController.cs | 2 +- DysonNetwork.Sphere/Publisher/PublisherService.cs | 4 ++-- DysonNetwork.Sphere/Realm/Realm.cs | 3 +-- DysonNetwork.Sphere/Realm/RealmService.cs | 5 +++-- DysonNetwork.Sphere/Sticker/Sticker.cs | 3 +-- DysonNetwork.Sphere/WebReader/WebArticle.cs | 3 +-- 12 files changed, 16 insertions(+), 21 deletions(-) diff --git a/DysonNetwork.Sphere/Chat/ChatRoom.cs b/DysonNetwork.Sphere/Chat/ChatRoom.cs index 73eb46e..639493a 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoom.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoom.cs @@ -2,9 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using DysonNetwork.Shared.Data; -using DysonNetwork.Shared.Proto; using NodaTime; -using Account = DysonNetwork.Pass.Account.Account; namespace DysonNetwork.Sphere.Chat; @@ -75,7 +73,7 @@ public class ChatMember : ModelBase public Guid ChatRoomId { get; set; } public ChatRoom ChatRoom { get; set; } = null!; public Guid AccountId { get; set; } - [NotMapped] public Account? Account { get; set; } + [NotMapped] public AccountReference? Account { get; set; } [NotMapped] public AccountStatusReference? Status { get; set; } [MaxLength(1024)] public string? Nick { get; set; } @@ -108,7 +106,7 @@ public class ChatMemberTransmissionObject : ModelBase public Guid Id { get; set; } public Guid ChatRoomId { get; set; } public Guid AccountId { get; set; } - [NotMapped] public Account Account { get; set; } = null!; + [NotMapped] public AccountReference Account { get; set; } = null!; [MaxLength(1024)] public string? Nick { get; set; } diff --git a/DysonNetwork.Sphere/Chat/ChatRoomService.cs b/DysonNetwork.Sphere/Chat/ChatRoomService.cs index 0a4af47..a3a0ada 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoomService.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoomService.cs @@ -2,7 +2,7 @@ using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Registry; using Microsoft.EntityFrameworkCore; using NodaTime; -using Account = DysonNetwork.Pass.Account.Account; +using Account = DysonNetwork.Shared.Data.AccountReference; namespace DysonNetwork.Sphere.Chat; diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index 7775f38..1b7b4f2 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -161,7 +161,6 @@ - diff --git a/DysonNetwork.Sphere/Poll/Poll.cs b/DysonNetwork.Sphere/Poll/Poll.cs index e7778d6..fac9b6a 100644 --- a/DysonNetwork.Sphere/Poll/Poll.cs +++ b/DysonNetwork.Sphere/Poll/Poll.cs @@ -2,7 +2,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json; using System.Text.Json.Serialization; -using DysonNetwork.Pass.Account; using DysonNetwork.Shared.Data; using NodaTime; @@ -64,5 +63,5 @@ public class PollAnswer : ModelBase public Guid AccountId { get; set; } public Guid PollId { get; set; } [JsonIgnore] public Poll? Poll { get; set; } - [NotMapped] public Account? Account { get; set; } + [NotMapped] public AccountReference? Account { get; set; } } diff --git a/DysonNetwork.Sphere/Poll/PollController.cs b/DysonNetwork.Sphere/Poll/PollController.cs index 46a492d..e56e2ce 100644 --- a/DysonNetwork.Sphere/Poll/PollController.cs +++ b/DysonNetwork.Sphere/Poll/PollController.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; +using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; using Microsoft.AspNetCore.Authorization; @@ -116,7 +117,7 @@ public class PollController( { var protoValue = answeredAccounts.FirstOrDefault(a => a.Id == answer.AccountId.ToString()); if (protoValue is not null) - answer.Account = Pass.Account.Account.FromProtoValue(protoValue); + answer.Account = AccountReference.FromProtoValue(protoValue); } } diff --git a/DysonNetwork.Sphere/Publisher/Publisher.cs b/DysonNetwork.Sphere/Publisher/Publisher.cs index 9f826d1..52b56fd 100644 --- a/DysonNetwork.Sphere/Publisher/Publisher.cs +++ b/DysonNetwork.Sphere/Publisher/Publisher.cs @@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore; using NodaTime; using NodaTime.Serialization.Protobuf; using VerificationMark = DysonNetwork.Shared.Data.VerificationMark; -using Account = DysonNetwork.Pass.Account.Account; +using Account = DysonNetwork.Shared.Data.AccountReference; namespace DysonNetwork.Sphere.Publisher; diff --git a/DysonNetwork.Sphere/Publisher/PublisherController.cs b/DysonNetwork.Sphere/Publisher/PublisherController.cs index fcf3fb2..70e8784 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherController.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherController.cs @@ -34,7 +34,7 @@ public class PublisherController( var account = await accounts.GetAccountAsync( new GetAccountRequest { Id = publisher.AccountId.Value.ToString() } ); - publisher.Account = Pass.Account.Account.FromProtoValue(account); + publisher.Account = AccountReference.FromProtoValue(account); return Ok(publisher); } diff --git a/DysonNetwork.Sphere/Publisher/PublisherService.cs b/DysonNetwork.Sphere/Publisher/PublisherService.cs index 1165b24..27ba956 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherService.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherService.cs @@ -382,7 +382,7 @@ public class PublisherService( public async Task LoadMemberAccount(PublisherMember member) { var account = await accountsHelper.GetAccount(member.AccountId); - member.Account = Pass.Account.Account.FromProtoValue(account); + member.Account = AccountReference.FromProtoValue(account); return member; } @@ -394,7 +394,7 @@ public class PublisherService( return members.Select(m => { if (accounts.TryGetValue(m.AccountId, out var account)) - m.Account = Pass.Account.Account.FromProtoValue(account); + m.Account = AccountReference.FromProtoValue(account); return m; }).ToList(); } diff --git a/DysonNetwork.Sphere/Realm/Realm.cs b/DysonNetwork.Sphere/Realm/Realm.cs index af8aafd..8d733d4 100644 --- a/DysonNetwork.Sphere/Realm/Realm.cs +++ b/DysonNetwork.Sphere/Realm/Realm.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; -using DysonNetwork.Pass.Account; using DysonNetwork.Shared.Data; using DysonNetwork.Sphere.Chat; using Microsoft.EntityFrameworkCore; @@ -48,7 +47,7 @@ public class RealmMember : ModelBase public Guid RealmId { get; set; } public Realm Realm { get; set; } = null!; public Guid AccountId { get; set; } - [NotMapped] public Account? Account { get; set; } + [NotMapped] public AccountReference? Account { get; set; } [NotMapped] public AccountStatusReference? Status { get; set; } public int Role { get; set; } = RealmMemberRole.Normal; diff --git a/DysonNetwork.Sphere/Realm/RealmService.cs b/DysonNetwork.Sphere/Realm/RealmService.cs index 6afc257..9ee2153 100644 --- a/DysonNetwork.Sphere/Realm/RealmService.cs +++ b/DysonNetwork.Sphere/Realm/RealmService.cs @@ -1,5 +1,6 @@ using DysonNetwork.Shared; using DysonNetwork.Shared.Cache; +using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; using DysonNetwork.Sphere.Localization; @@ -73,7 +74,7 @@ public class RealmService( public async Task LoadMemberAccount(RealmMember member) { var account = await accountsHelper.GetAccount(member.AccountId); - member.Account = Pass.Account.Account.FromProtoValue(account); + member.Account = AccountReference.FromProtoValue(account); return member; } @@ -85,7 +86,7 @@ public class RealmService( return members.Select(m => { if (accounts.TryGetValue(m.AccountId, out var account)) - m.Account = Pass.Account.Account.FromProtoValue(account); + m.Account = AccountReference.FromProtoValue(account); return m; }).ToList(); } diff --git a/DysonNetwork.Sphere/Sticker/Sticker.cs b/DysonNetwork.Sphere/Sticker/Sticker.cs index dd125e0..c145656 100644 --- a/DysonNetwork.Sphere/Sticker/Sticker.cs +++ b/DysonNetwork.Sphere/Sticker/Sticker.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; -using DysonNetwork.Pass.Account; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -46,5 +45,5 @@ public class StickerPackOwnership : ModelBase public Guid PackId { get; set; } public StickerPack Pack { get; set; } = null!; public Guid AccountId { get; set; } - [NotMapped] public Account Account { get; set; } = null!; + [NotMapped] public AccountReference Account { get; set; } = null!; } diff --git a/DysonNetwork.Sphere/WebReader/WebArticle.cs b/DysonNetwork.Sphere/WebReader/WebArticle.cs index cfa9049..1945804 100644 --- a/DysonNetwork.Sphere/WebReader/WebArticle.cs +++ b/DysonNetwork.Sphere/WebReader/WebArticle.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; -using DysonNetwork.Pass.Account; using DysonNetwork.Shared.Data; namespace DysonNetwork.Sphere.WebReader; @@ -54,5 +53,5 @@ public class WebFeedSubscription : ModelBase public Guid FeedId { get; set; } public WebFeed Feed { get; set; } = null!; public Guid AccountId { get; set; } - [NotMapped] public Account Account { get; set; } = null!; + [NotMapped] public AccountReference Account { get; set; } = null!; } \ No newline at end of file From d8f57161aeed1a199e6b0def78883d5a8b7b62ec Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 16 Sep 2025 23:36:26 +0800 Subject: [PATCH 08/11] :hammer: Add aspire build workflow --- .github/workflows/docker-build.yml | 190 +++++------------------------ DysonNetwork.sln.DotSettings.user | 2 + publish/.env | 35 ++++++ publish/docker-compose.yaml | 181 +++++++++++++++++++++++++++ settings/develop.json | 26 ++++ settings/drive.json | 133 ++++++++++++++++++++ settings/pass.json | 88 +++++++++++++ settings/ring.json | 50 ++++++++ settings/sphere.json | 41 +++++++ 9 files changed, 587 insertions(+), 159 deletions(-) create mode 100644 publish/.env create mode 100644 publish/docker-compose.yaml create mode 100644 settings/develop.json create mode 100644 settings/drive.json create mode 100644 settings/pass.json create mode 100644 settings/ring.json create mode 100644 settings/sphere.json diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f4f3bac..14c1e75 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,4 +1,4 @@ -name: Build and Push Microservices +name: Aspire Publish Workflow on: push: @@ -7,7 +7,7 @@ on: workflow_dispatch: jobs: - build-sphere: + publish: runs-on: ubuntu-latest permissions: contents: read @@ -15,174 +15,46 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Sphere Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Sphere/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-sphere:latest - platforms: linux/amd64 - build-pass: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Pass Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Pass/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-pass:latest - platforms: linux/amd64 + dotnet-version: "9.0.x" - build-ring: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Pusher Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Ring/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-ring:latest - platforms: linux/amd64 - build-drive: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Drive Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Drive/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-drive:latest - platforms: linux/amd64 + - name: Install Aspire CLI + run: dotnet tool install -g Aspire.Cli --prerelease - build-gateway: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Gateway Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Gateway/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-gateway:latest - platforms: linux/amd64 + - name: Build and Publish Aspire Application + run: aspire publish --project ./DysonNetwork.Control/DysonNetwork.Control.csproj --output publish - build-develop: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Tag and Push Images + run: | + IMAGES=( "sphere" "pass" "ring" "drive" "develop" ) + + for image in "${IMAGES[@]}"; do + IMAGE_NAME="ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-$image:latest" + SOURCE_IMAGE_NAME="$image:latest" # Aspire's default local image name + + echo "Tagging and pushing $SOURCE_IMAGE_NAME to $IMAGE_NAME..." + docker tag $SOURCE_IMAGE_NAME $IMAGE_NAME + docker push $IMAGE_NAME + done + + - name: Upload Aspire Publish Directory + uses: actions/upload-artifact@v3 with: - fetch-depth: 0 - - name: Setup NBGV - uses: dotnet/nbgv@master - id: nbgv - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + name: aspire-publish-output + path: ./publish/ + + - name: Upload Docker Compose file + uses: actions/upload-artifact@v3 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push DysonNetwork.Develop Docker image - uses: docker/build-push-action@v6 - with: - file: DysonNetwork.Develop/Dockerfile - context: . - push: true - tags: ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-develop:latest - platforms: linux/amd64 + name: docker-compose-output + path: ./publish/docker-compose.yml diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 383843d..72dd347 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -22,6 +22,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -33,6 +34,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/publish/.env b/publish/.env new file mode 100644 index 0000000..13c2b1d --- /dev/null +++ b/publish/.env @@ -0,0 +1,35 @@ +# Default container port for ring +RING_PORT=8080 + +# Default container port for pass +PASS_PORT=8080 + +# Default container port for drive +DRIVE_PORT=8080 + +# Default container port for sphere +SPHERE_PORT=8080 + +# Default container port for develop +DEVELOP_PORT=8080 + +# Parameter cache-password +CACHE_PASSWORD=KS3jSPaU9e + +# Parameter queue-password +QUEUE_PASSWORD=8xEECa4ckz + +# Container image name for ring +RING_IMAGE=ring:latest + +# Container image name for pass +PASS_IMAGE=pass:latest + +# Container image name for drive +DRIVE_IMAGE=drive:latest + +# Container image name for sphere +SPHERE_IMAGE=sphere:latest + +# Container image name for develop +DEVELOP_IMAGE=develop:latest diff --git a/publish/docker-compose.yaml b/publish/docker-compose.yaml new file mode 100644 index 0000000..d439bed --- /dev/null +++ b/publish/docker-compose.yaml @@ -0,0 +1,181 @@ +services: + docker-compose-dashboard: + image: "mcr.microsoft.com/dotnet/nightly/aspire-dashboard:latest" + expose: + - "18888" + - "18889" + networks: + - "aspire" + restart: "always" + cache: + image: "docker.io/library/redis:7.4" + command: + - "-c" + - "redis-server --requirepass $$REDIS_PASSWORD" + entrypoint: + - "/bin/sh" + environment: + REDIS_PASSWORD: "${CACHE_PASSWORD}" + expose: + - "6379" + networks: + - "aspire" + queue: + image: "docker.io/library/nats:2.11" + command: + - "--user" + - "nats" + - "--pass" + - "${QUEUE_PASSWORD}" + - "-js" + expose: + - "4222" + networks: + - "aspire" + ring: + image: "${RING_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${RING_PORT}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "ring" + expose: + - "${RING_PORT}" + networks: + - "aspire" + pass: + image: "${PASS_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${PASS_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "pass" + expose: + - "${PASS_PORT}" + networks: + - "aspire" + drive: + image: "${DRIVE_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${DRIVE_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "drive" + expose: + - "${DRIVE_PORT}" + networks: + - "aspire" + sphere: + image: "${SPHERE_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${SPHERE_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "sphere" + expose: + - "${SPHERE_PORT}" + networks: + - "aspire" + develop: + image: "${DEVELOP_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${DEVELOP_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "develop" + expose: + - "${DEVELOP_PORT}" + networks: + - "aspire" + gateway: + image: "mcr.microsoft.com/dotnet/nightly/yarp:2.3.0-preview.4" + command: + - "/app/yarp.dll" + entrypoint: + - "dotnet" + environment: + ASPNETCORE_ENVIRONMENT: "Production" + services__ring__http__0: "http://ring:${RING_PORT}" + REVERSEPROXY__ROUTES__route0__MATCH__PATH: "/ws" + REVERSEPROXY__ROUTES__route0__CLUSTERID: "cluster_ring" + REVERSEPROXY__ROUTES__route1__MATCH__PATH: "/ring/{**catch-all}" + REVERSEPROXY__ROUTES__route1__CLUSTERID: "cluster_ring" + REVERSEPROXY__ROUTES__route1__TRANSFORMS__0__PathRemovePrefix: "/ring" + REVERSEPROXY__ROUTES__route1__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route2__MATCH__PATH: "/.well-known/openid-configuration" + REVERSEPROXY__ROUTES__route2__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route3__MATCH__PATH: "/.well-known/jwks" + REVERSEPROXY__ROUTES__route3__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route4__MATCH__PATH: "/id/{**catch-all}" + REVERSEPROXY__ROUTES__route4__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route4__TRANSFORMS__0__PathRemovePrefix: "/id" + REVERSEPROXY__ROUTES__route4__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route5__MATCH__PATH: "/api/tus" + REVERSEPROXY__ROUTES__route5__CLUSTERID: "cluster_drive" + REVERSEPROXY__ROUTES__route6__MATCH__PATH: "/drive/{**catch-all}" + REVERSEPROXY__ROUTES__route6__CLUSTERID: "cluster_drive" + REVERSEPROXY__ROUTES__route6__TRANSFORMS__0__PathRemovePrefix: "/drive" + REVERSEPROXY__ROUTES__route6__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route7__MATCH__PATH: "/sphere/{**catch-all}" + REVERSEPROXY__ROUTES__route7__CLUSTERID: "cluster_sphere" + REVERSEPROXY__ROUTES__route7__TRANSFORMS__0__PathRemovePrefix: "/sphere" + REVERSEPROXY__ROUTES__route7__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route8__MATCH__PATH: "/develop/{**catch-all}" + REVERSEPROXY__ROUTES__route8__CLUSTERID: "cluster_develop" + REVERSEPROXY__ROUTES__route8__TRANSFORMS__0__PathRemovePrefix: "/develop" + REVERSEPROXY__ROUTES__route8__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__CLUSTERS__cluster_ring__DESTINATIONS__destination1__ADDRESS: "http://_http.ring" + REVERSEPROXY__CLUSTERS__cluster_pass__DESTINATIONS__destination1__ADDRESS: "http://_http.pass" + REVERSEPROXY__CLUSTERS__cluster_drive__DESTINATIONS__destination1__ADDRESS: "http://_http.drive" + REVERSEPROXY__CLUSTERS__cluster_sphere__DESTINATIONS__destination1__ADDRESS: "http://_http.sphere" + REVERSEPROXY__CLUSTERS__cluster_develop__DESTINATIONS__destination1__ADDRESS: "http://_http.develop" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__drive__http__0: "http://drive:${DRIVE_PORT}" + services__sphere__http__0: "http://sphere:${SPHERE_PORT}" + services__develop__http__0: "http://develop:${DEVELOP_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "gateway" + expose: + - "5000" + networks: + - "aspire" +networks: + aspire: + driver: "bridge" diff --git a/settings/develop.json b/settings/develop.json new file mode 100644 index 0000000..b81e2d8 --- /dev/null +++ b/settings/develop.json @@ -0,0 +1,26 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5071", + "SiteUrl": "https://solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=localhost;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "KnownProxies": [ + "127.0.0.1", + "::1" + ], + "Etcd": { + "Insecure": true + }, + "Service": { + "Name": "DysonNetwork.Develop", + "Url": "https://localhost:7192" + } +} diff --git a/settings/drive.json b/settings/drive.json new file mode 100644 index 0000000..f025412 --- /dev/null +++ b/settings/drive.json @@ -0,0 +1,133 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5090", + "GatewayUrl": "http://localhost:5094", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Authentication": { + "Schemes": { + "Bearer": { + "ValidAudiences": [ + "http://localhost:5071", + "https://localhost:7099" + ], + "ValidIssuer": "solar-network" + } + } + }, + "AuthToken": { + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem" + }, + "OidcProvider": { + "IssuerUri": "https://nt.solian.app", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem", + "AccessTokenLifetime": "01:00:00", + "RefreshTokenLifetime": "30.00:00:00", + "AuthorizationCodeLifetime": "00:30:00", + "RequireHttpsMetadata": true + }, + "Tus": { + "StorePath": "Uploads" + }, + "Storage": { + "PreferredRemote": "2adceae3-981a-4564-9b8d-5d71a211c873", + "Remote": [ + { + "Id": "minio", + "Label": "Minio", + "Region": "auto", + "Bucket": "solar-network-development", + "Endpoint": "localhost:9000", + "SecretId": "littlesheep", + "SecretKey": "password", + "EnabledSigned": true, + "EnableSsl": false + }, + { + "Id": "cloudflare", + "Label": "Cloudflare R2", + "Region": "auto", + "Bucket": "solar-network", + "Endpoint": "0a70a6d1b7128888c823359d0008f4e1.r2.cloudflarestorage.com", + "SecretId": "8ff5d06c7b1639829d60bc6838a542e6", + "SecretKey": "fd58158c5201be16d1872c9209d9cf199421dae3c2f9972f94b2305976580d67", + "EnableSigned": true, + "EnableSsl": true + } + ] + }, + "Captcha": { + "Provider": "cloudflare", + "ApiKey": "0x4AAAAAABCDUdOujj4feOb_", + "ApiSecret": "0x4AAAAAABCDUWABiJQweqlB7tYq-IqIm8U" + }, + "Notifications": { + "Topic": "dev.solsynth.solian", + "Endpoint": "http://localhost:8088" + }, + "Email": { + "Server": "smtp4dev.orb.local", + "Port": 25, + "UseSsl": false, + "Username": "no-reply@mail.solsynth.dev", + "Password": "password", + "FromAddress": "no-reply@mail.solsynth.dev", + "FromName": "Alphabot", + "SubjectPrefix": "Solar Network" + }, + "RealtimeChat": { + "Endpoint": "https://solar-network-im44o8gq.livekit.cloud", + "ApiKey": "APIs6TiL8wj3A4j", + "ApiSecret": "SffxRneIwTnlHPtEf3zicmmv3LUEl7xXael4PvWZrEhE" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "Oidc": { + "Google": { + "ClientId": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com", + "ClientSecret": "" + }, + "Apple": { + "ClientId": "dev.solsynth.solian", + "TeamId": "W7HPZ53V6B", + "KeyId": "B668YP4KBG", + "PrivateKeyPath": "./Keys/Solarpass.p8" + }, + "Microsoft": { + "ClientId": "YOUR_MICROSOFT_CLIENT_ID", + "ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET", + "DiscoveryEndpoint": "YOUR_MICROSOFT_DISCOVERY_ENDPOINT" + } + }, + "Payment": { + "Auth": { + "Afdian": "" + }, + "Subscriptions": { + "Afdian": { + "7d17aae23c9611f0b5705254001e7c00": "solian.stellar.primary", + "7dfae4743c9611f0b3a55254001e7c00": "solian.stellar.nova", + "141713ee3d6211f085b352540025c377": "solian.stellar.supernova" + } + } + }, + "KnownProxies": [ + "127.0.0.1", + "::1" + ], + "Service": { + "Name": "DysonNetwork.Drive", + "Url": "https://localhost:7092" + } +} diff --git a/settings/pass.json b/settings/pass.json new file mode 100644 index 0000000..066d1a3 --- /dev/null +++ b/settings/pass.json @@ -0,0 +1,88 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5216", + "SiteUrl": "https://id.solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Authentication": { + "Schemes": { + "Bearer": { + "ValidAudiences": [ + "http://localhost:5071", + "https://localhost:7099" + ], + "ValidIssuer": "solar-network" + } + } + }, + "AuthToken": { + "CookieDomain": "localhost", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem" + }, + "OidcProvider": { + "IssuerUri": "https://nt.solian.app", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem", + "AccessTokenLifetime": "01:00:00", + "RefreshTokenLifetime": "30.00:00:00", + "AuthorizationCodeLifetime": "00:30:00", + "RequireHttpsMetadata": true + }, + "Captcha": { + "Provider": "cloudflare", + "ApiKey": "0x4AAAAAABCDUdOujj4feOb_", + "ApiSecret": "0x4AAAAAABCDUWABiJQweqlB7tYq-IqIm8U" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "Oidc": { + "Google": { + "ClientId": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com", + "ClientSecret": "" + }, + "Apple": { + "ClientId": "dev.solsynth.solian", + "TeamId": "W7HPZ53V6B", + "KeyId": "B668YP4KBG", + "PrivateKeyPath": "./Keys/Solarpass.p8" + }, + "Microsoft": { + "ClientId": "YOUR_MICROSOFT_CLIENT_ID", + "ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET", + "DiscoveryEndpoint": "YOUR_MICROSOFT_DISCOVERY_ENDPOINT" + } + }, + "Payment": { + "Auth": { + "Afdian": "" + }, + "Subscriptions": { + "Afdian": { + "7d17aae23c9611f0b5705254001e7c00": "solian.stellar.primary", + "7dfae4743c9611f0b3a55254001e7c00": "solian.stellar.nova", + "141713ee3d6211f085b352540025c377": "solian.stellar.supernova" + } + } + }, + "KnownProxies": [ + "127.0.0.1", + "::1" + ], + "Service": { + "Name": "DysonNetwork.Pass", + "Url": "https://localhost:7058" + }, + "Etcd": { + "Insecure": true + } +} diff --git a/settings/ring.json b/settings/ring.json new file mode 100644 index 0000000..2ab4e9e --- /dev/null +++ b/settings/ring.json @@ -0,0 +1,50 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5212", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=localhost;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Notifications": { + "Push": { + "Production": true, + "Google": "./Keys/Solian.json", + "Apple": { + "PrivateKey": "./Keys/Solian.p8", + "PrivateKeyId": "4US4KSX4W6", + "TeamId": "W7HPZ53V6B", + "BundleIdentifier": "dev.solsynth.solian" + } + } + }, + "Email": { + "Server": "smtp4dev.orb.local", + "Port": 25, + "UseSsl": false, + "Username": "no-reply@mail.solsynth.dev", + "Password": "password", + "FromAddress": "no-reply@mail.solsynth.dev", + "FromName": "Alphabot", + "SubjectPrefix": "Solar Network" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "KnownProxies": [ + "127.0.0.1", + "::1" + ], + "Service": { + "Name": "DysonNetwork.Ring", + "Url": "https://localhost:7259" + }, + "Etcd": { + "Insecure": true + } +} diff --git a/settings/sphere.json b/settings/sphere.json new file mode 100644 index 0000000..1157d91 --- /dev/null +++ b/settings/sphere.json @@ -0,0 +1,41 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5071", + "SiteUrl": "https://solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=localhost;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "RealtimeChat": { + "Endpoint": "https://solar-network-im44o8gq.livekit.cloud", + "ApiKey": "", + "ApiSecret": "" + }, + "Translation": { + "Provider": "Tencent", + "Region": "ap-hongkong", + "ProjectId": "0", + "SecretId": "", + "SecretKey": "" + }, + "KnownProxies": [ + "127.0.0.1", + "::1" + ], + "Etcd": { + "Insecure": true + }, + "Service": { + "Name": "DysonNetwork.Sphere", + "Url": "https://localhost:7099" + } +} From 71fe2a30e75eb0f30d8717f27edd36f8c2ad385c Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 16 Sep 2025 23:36:55 +0800 Subject: [PATCH 09/11] :necktie: Change the version tag for aspire based images --- .github/workflows/docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 14c1e75..f78b693 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -39,7 +39,7 @@ jobs: IMAGES=( "sphere" "pass" "ring" "drive" "develop" ) for image in "${IMAGES[@]}"; do - IMAGE_NAME="ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-$image:latest" + IMAGE_NAME="ghcr.io/${{ vars.PACKAGE_OWNER }}/dyson-$image:alpha" SOURCE_IMAGE_NAME="$image:latest" # Aspire's default local image name echo "Tagging and pushing $SOURCE_IMAGE_NAME to $IMAGE_NAME..." From 3d47b4e44e8fe15eb22ca9fe4576e94f06cb7368 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 17 Sep 2025 00:57:24 +0800 Subject: [PATCH 10/11] :arrow_up: Save progress and say goodbye --- .env | 35 ++++ DysonNetwork.Control/AppHost.cs | 2 +- .../DysonNetwork.Control.csproj | 1 - compose.yaml | 86 -------- docker-compose.yaml | 194 ++++++++++++++++++ publish/settings/develop.json | 23 +++ publish/settings/drive.json | 127 ++++++++++++ publish/settings/pass.json | 82 ++++++++ publish/settings/ring.json | 47 +++++ publish/settings/sphere.json | 38 ++++ settings/develop.json | 7 +- settings/drive.json | 16 +- settings/pass.json | 12 +- settings/ring.json | 11 +- settings/sphere.json | 9 +- 15 files changed, 564 insertions(+), 126 deletions(-) create mode 100644 .env delete mode 100644 compose.yaml create mode 100644 docker-compose.yaml create mode 100644 publish/settings/develop.json create mode 100644 publish/settings/drive.json create mode 100644 publish/settings/pass.json create mode 100644 publish/settings/ring.json create mode 100644 publish/settings/sphere.json diff --git a/.env b/.env new file mode 100644 index 0000000..13c2b1d --- /dev/null +++ b/.env @@ -0,0 +1,35 @@ +# Default container port for ring +RING_PORT=8080 + +# Default container port for pass +PASS_PORT=8080 + +# Default container port for drive +DRIVE_PORT=8080 + +# Default container port for sphere +SPHERE_PORT=8080 + +# Default container port for develop +DEVELOP_PORT=8080 + +# Parameter cache-password +CACHE_PASSWORD=KS3jSPaU9e + +# Parameter queue-password +QUEUE_PASSWORD=8xEECa4ckz + +# Container image name for ring +RING_IMAGE=ring:latest + +# Container image name for pass +PASS_IMAGE=pass:latest + +# Container image name for drive +DRIVE_IMAGE=drive:latest + +# Container image name for sphere +SPHERE_IMAGE=sphere:latest + +# Container image name for develop +DEVELOP_IMAGE=develop:latest diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index 8c18814..7a64e5c 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -64,4 +64,4 @@ builder.AddYarp("gateway") builder.AddDockerComposeEnvironment("docker-compose"); -builder.Build().Run(); \ No newline at end of file +builder.Build().Run(); diff --git a/DysonNetwork.Control/DysonNetwork.Control.csproj b/DysonNetwork.Control/DysonNetwork.Control.csproj index f72833a..970a972 100644 --- a/DysonNetwork.Control/DysonNetwork.Control.csproj +++ b/DysonNetwork.Control/DysonNetwork.Control.csproj @@ -24,7 +24,6 @@ - diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index fee2329..0000000 --- a/compose.yaml +++ /dev/null @@ -1,86 +0,0 @@ -services: - etcd: - image: bitnami/etcd:latest - ports: - - "2379:2379" - - "2380:2380" - environment: - - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379 - - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 - - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380 - - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd:2380 - - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster - - ETCD_INITIAL_CLUSTER_STATE=new - - ETCD_INITIAL_CLUSTER=etcd=http://etcd:2380 - healthcheck: - test: ["CMD", "etcdctl", "get", "/health"] - interval: 5s - timeout: 5s - retries: 5 - - gateway: - image: xsheep2010/dyson-gateway:latest - ports: - - "8000:8080" - environment: - - ConnectionStrings__Etcd=http://etcd:2379 - - Etcd__Insecure=true - - Service__Name=DysonNetwork.Gateway - - Service__Url=http://gateway:8080 - depends_on: - etcd: - condition: service_healthy - - drive: - image: xsheep2010/dyson-drive:latest - ports: - - "8001:8080" - environment: - - ConnectionStrings__Etcd=http://etcd:2379 - - Etcd__Insecure=true - - Service__Name=DysonNetwork.Drive - - Service__Url=http://drive:8080 - depends_on: - etcd: - condition: service_healthy - - pass: - image: xsheep2010/dyson-pass:latest - ports: - - "8002:8080" - environment: - - ConnectionStrings__Etcd=http://etcd:2379 - - Etcd__Insecure=true - - Service__Name=DysonNetwork.Pass - - Service__Url=http://pass:8080 - depends_on: - etcd: - condition: service_healthy - - ring: - image: xsheep2010/dyson-ring:latest - ports: - - "8003:8080" - environment: - - ConnectionStrings__Etcd=http://etcd:2379 - - Etcd__Insecure=true - - Service__Name=DysonNetwork.Ring - - Service__Url=http://ring:8080 - depends_on: - etcd: - condition: service_healthy - - sphere: - image: xsheep2010/dyson-sphere:latest - ports: - - "8004:8080" - environment: - - ConnectionStrings__Etcd=http://etcd:2379 - - Etcd__Insecure=true - - Service__Name=DysonNetwork.Sphere - - Service__Url=http://sphere:8080 - volumes: - - "./keys:/app/keys" - depends_on: - etcd: - condition: service_healthy \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..45745be --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,194 @@ +services: + aspire-dashboard: + image: "mcr.microsoft.com/dotnet/nightly/aspire-dashboard:latest" + expose: + - "18888" + - "18889" + networks: + - "aspire" + restart: "always" + cache: + image: "docker.io/library/redis:7.4" + command: + - "-c" + - "redis-server --requirepass $$REDIS_PASSWORD" + entrypoint: + - "/bin/sh" + environment: + REDIS_PASSWORD: "${CACHE_PASSWORD}" + expose: + - "6379" + networks: + - "aspire" + queue: + image: "docker.io/library/nats:2.11" + command: + - "--user" + - "nats" + - "--pass" + - "${QUEUE_PASSWORD}" + - "-js" + expose: + - "4222" + networks: + - "aspire" + ring: + image: "${RING_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${RING_PORT}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "ring" + volumes: + - "./Keys:/app/keys" + - "./settings/ring.json:/app/appsettings.json" + expose: + - "${RING_PORT}" + networks: + - "aspire" + pass: + image: "${PASS_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${PASS_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "pass" + volumes: + - "./Keys:/app/keys" + - "./settings/pass.json:/app/appsettings.json" + expose: + - "${PASS_PORT}" + networks: + - "aspire" + drive: + image: "${DRIVE_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${DRIVE_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "drive" + expose: + - "${DRIVE_PORT}" + volumes: + - "./settings/drive.json:/app/appsettings.json" + networks: + - "aspire" + sphere: + image: "${SPHERE_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${SPHERE_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "sphere" + volumes: + - "./Keys:/app/keys" + - "./settings/sphere.json:/app/appsettings.json" + expose: + - "${SPHERE_PORT}" + networks: + - "aspire" + develop: + image: "${DEVELOP_IMAGE}" + environment: + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true" + OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" + ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" + HTTP_PORTS: "${DEVELOP_PORT}" + ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__ring__http__0: "http://ring:${RING_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "develop" + expose: + - "${DEVELOP_PORT}" + networks: + - "aspire" + gateway: + image: "mcr.microsoft.com/dotnet/nightly/yarp:2.3.0-preview.4" + command: + - "/app/yarp.dll" + entrypoint: + - "dotnet" + environment: + ASPNETCORE_ENVIRONMENT: "Production" + services__ring__http__0: "http://ring:${RING_PORT}" + REVERSEPROXY__ROUTES__route0__MATCH__PATH: "/ws" + REVERSEPROXY__ROUTES__route0__CLUSTERID: "cluster_ring" + REVERSEPROXY__ROUTES__route1__MATCH__PATH: "/ring/{**catch-all}" + REVERSEPROXY__ROUTES__route1__CLUSTERID: "cluster_ring" + REVERSEPROXY__ROUTES__route1__TRANSFORMS__0__PathRemovePrefix: "/ring" + REVERSEPROXY__ROUTES__route1__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route2__MATCH__PATH: "/.well-known/openid-configuration" + REVERSEPROXY__ROUTES__route2__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route3__MATCH__PATH: "/.well-known/jwks" + REVERSEPROXY__ROUTES__route3__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route4__MATCH__PATH: "/id/{**catch-all}" + REVERSEPROXY__ROUTES__route4__CLUSTERID: "cluster_pass" + REVERSEPROXY__ROUTES__route4__TRANSFORMS__0__PathRemovePrefix: "/id" + REVERSEPROXY__ROUTES__route4__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route5__MATCH__PATH: "/api/tus" + REVERSEPROXY__ROUTES__route5__CLUSTERID: "cluster_drive" + REVERSEPROXY__ROUTES__route6__MATCH__PATH: "/drive/{**catch-all}" + REVERSEPROXY__ROUTES__route6__CLUSTERID: "cluster_drive" + REVERSEPROXY__ROUTES__route6__TRANSFORMS__0__PathRemovePrefix: "/drive" + REVERSEPROXY__ROUTES__route6__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route7__MATCH__PATH: "/sphere/{**catch-all}" + REVERSEPROXY__ROUTES__route7__CLUSTERID: "cluster_sphere" + REVERSEPROXY__ROUTES__route7__TRANSFORMS__0__PathRemovePrefix: "/sphere" + REVERSEPROXY__ROUTES__route7__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__ROUTES__route8__MATCH__PATH: "/develop/{**catch-all}" + REVERSEPROXY__ROUTES__route8__CLUSTERID: "cluster_develop" + REVERSEPROXY__ROUTES__route8__TRANSFORMS__0__PathRemovePrefix: "/develop" + REVERSEPROXY__ROUTES__route8__TRANSFORMS__1__PathPrefix: "/api" + REVERSEPROXY__CLUSTERS__cluster_ring__DESTINATIONS__destination1__ADDRESS: "http://_http.ring" + REVERSEPROXY__CLUSTERS__cluster_pass__DESTINATIONS__destination1__ADDRESS: "http://_http.pass" + REVERSEPROXY__CLUSTERS__cluster_drive__DESTINATIONS__destination1__ADDRESS: "http://_http.drive" + REVERSEPROXY__CLUSTERS__cluster_sphere__DESTINATIONS__destination1__ADDRESS: "http://_http.sphere" + REVERSEPROXY__CLUSTERS__cluster_develop__DESTINATIONS__destination1__ADDRESS: "http://_http.develop" + services__pass__http__0: "http://pass:${PASS_PORT}" + services__drive__http__0: "http://drive:${DRIVE_PORT}" + services__sphere__http__0: "http://sphere:${SPHERE_PORT}" + services__develop__http__0: "http://develop:${DEVELOP_PORT}" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "gateway" + expose: + - "5000" + ports: + - "5001:5000" + networks: + - "aspire" +networks: + aspire: + driver: "bridge" diff --git a/publish/settings/develop.json b/publish/settings/develop.json new file mode 100644 index 0000000..a70297c --- /dev/null +++ b/publish/settings/develop.json @@ -0,0 +1,23 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5071", + "SiteUrl": "https://solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=host.docker.internal;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "KnownProxies": ["127.0.0.1", "::1"], + "Etcd": { + "Insecure": true + }, + "Service": { + "Name": "DysonNetwork.Develop", + "Url": "https://localhost:7192" + } +} diff --git a/publish/settings/drive.json b/publish/settings/drive.json new file mode 100644 index 0000000..b3bc501 --- /dev/null +++ b/publish/settings/drive.json @@ -0,0 +1,127 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5090", + "GatewayUrl": "http://localhost:5094", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=host.docker.internal;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Authentication": { + "Schemes": { + "Bearer": { + "ValidAudiences": ["http://localhost:5071", "https://localhost:7099"], + "ValidIssuer": "solar-network" + } + } + }, + "AuthToken": { + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem" + }, + "OidcProvider": { + "IssuerUri": "https://nt.solian.app", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem", + "AccessTokenLifetime": "01:00:00", + "RefreshTokenLifetime": "30.00:00:00", + "AuthorizationCodeLifetime": "00:30:00", + "RequireHttpsMetadata": true + }, + "Tus": { + "StorePath": "Uploads" + }, + "Storage": { + "PreferredRemote": "2adceae3-981a-4564-9b8d-5d71a211c873", + "Remote": [ + { + "Id": "minio", + "Label": "Minio", + "Region": "auto", + "Bucket": "solar-network-development", + "Endpoint": "localhost:9000", + "SecretId": "littlesheep", + "SecretKey": "password", + "EnabledSigned": true, + "EnableSsl": false + }, + { + "Id": "cloudflare", + "Label": "Cloudflare R2", + "Region": "auto", + "Bucket": "solar-network", + "Endpoint": "0a70a6d1b7128888c823359d0008f4e1.r2.cloudflarestorage.com", + "SecretId": "8ff5d06c7b1639829d60bc6838a542e6", + "SecretKey": "fd58158c5201be16d1872c9209d9cf199421dae3c2f9972f94b2305976580d67", + "EnableSigned": true, + "EnableSsl": true + } + ] + }, + "Captcha": { + "Provider": "cloudflare", + "ApiKey": "0x4AAAAAABCDUdOujj4feOb_", + "ApiSecret": "0x4AAAAAABCDUWABiJQweqlB7tYq-IqIm8U" + }, + "Notifications": { + "Topic": "dev.solsynth.solian", + "Endpoint": "http://localhost:8088" + }, + "Email": { + "Server": "smtp4dev.orb.local", + "Port": 25, + "UseSsl": false, + "Username": "no-reply@mail.solsynth.dev", + "Password": "password", + "FromAddress": "no-reply@mail.solsynth.dev", + "FromName": "Alphabot", + "SubjectPrefix": "Solar Network" + }, + "RealtimeChat": { + "Endpoint": "https://solar-network-im44o8gq.livekit.cloud", + "ApiKey": "APIs6TiL8wj3A4j", + "ApiSecret": "SffxRneIwTnlHPtEf3zicmmv3LUEl7xXael4PvWZrEhE" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "Oidc": { + "Google": { + "ClientId": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com", + "ClientSecret": "" + }, + "Apple": { + "ClientId": "dev.solsynth.solian", + "TeamId": "W7HPZ53V6B", + "KeyId": "B668YP4KBG", + "PrivateKeyPath": "./Keys/Solarpass.p8" + }, + "Microsoft": { + "ClientId": "YOUR_MICROSOFT_CLIENT_ID", + "ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET", + "DiscoveryEndpoint": "YOUR_MICROSOFT_DISCOVERY_ENDPOINT" + } + }, + "Payment": { + "Auth": { + "Afdian": "" + }, + "Subscriptions": { + "Afdian": { + "7d17aae23c9611f0b5705254001e7c00": "solian.stellar.primary", + "7dfae4743c9611f0b3a55254001e7c00": "solian.stellar.nova", + "141713ee3d6211f085b352540025c377": "solian.stellar.supernova" + } + } + }, + "KnownProxies": ["127.0.0.1", "::1"], + "Service": { + "Name": "DysonNetwork.Drive", + "Url": "https://localhost:7092" + } +} diff --git a/publish/settings/pass.json b/publish/settings/pass.json new file mode 100644 index 0000000..dea5e59 --- /dev/null +++ b/publish/settings/pass.json @@ -0,0 +1,82 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5216", + "SiteUrl": "https://id.solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=host.docker.internal;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Authentication": { + "Schemes": { + "Bearer": { + "ValidAudiences": ["http://localhost:5071", "https://localhost:7099"], + "ValidIssuer": "solar-network" + } + } + }, + "AuthToken": { + "CookieDomain": "localhost", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem" + }, + "OidcProvider": { + "IssuerUri": "https://nt.solian.app", + "PublicKeyPath": "Keys/PublicKey.pem", + "PrivateKeyPath": "Keys/PrivateKey.pem", + "AccessTokenLifetime": "01:00:00", + "RefreshTokenLifetime": "30.00:00:00", + "AuthorizationCodeLifetime": "00:30:00", + "RequireHttpsMetadata": true + }, + "Captcha": { + "Provider": "cloudflare", + "ApiKey": "0x4AAAAAABCDUdOujj4feOb_", + "ApiSecret": "0x4AAAAAABCDUWABiJQweqlB7tYq-IqIm8U" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "Oidc": { + "Google": { + "ClientId": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com", + "ClientSecret": "" + }, + "Apple": { + "ClientId": "dev.solsynth.solian", + "TeamId": "W7HPZ53V6B", + "KeyId": "B668YP4KBG", + "PrivateKeyPath": "./Keys/Solarpass.p8" + }, + "Microsoft": { + "ClientId": "YOUR_MICROSOFT_CLIENT_ID", + "ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET", + "DiscoveryEndpoint": "YOUR_MICROSOFT_DISCOVERY_ENDPOINT" + } + }, + "Payment": { + "Auth": { + "Afdian": "" + }, + "Subscriptions": { + "Afdian": { + "7d17aae23c9611f0b5705254001e7c00": "solian.stellar.primary", + "7dfae4743c9611f0b3a55254001e7c00": "solian.stellar.nova", + "141713ee3d6211f085b352540025c377": "solian.stellar.supernova" + } + } + }, + "KnownProxies": ["127.0.0.1", "::1"], + "Service": { + "Name": "DysonNetwork.Pass", + "Url": "https://localhost:7058" + }, + "Etcd": { + "Insecure": true + } +} diff --git a/publish/settings/ring.json b/publish/settings/ring.json new file mode 100644 index 0000000..3a15453 --- /dev/null +++ b/publish/settings/ring.json @@ -0,0 +1,47 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5212", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=host.docker.internal;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "Notifications": { + "Push": { + "Production": true, + "Google": "./Keys/Solian.json", + "Apple": { + "PrivateKey": "./Keys/Solian.p8", + "PrivateKeyId": "4US4KSX4W6", + "TeamId": "W7HPZ53V6B", + "BundleIdentifier": "dev.solsynth.solian" + } + } + }, + "Email": { + "Server": "smtp4dev.orb.local", + "Port": 25, + "UseSsl": false, + "Username": "no-reply@mail.solsynth.dev", + "Password": "password", + "FromAddress": "no-reply@mail.solsynth.dev", + "FromName": "Alphabot", + "SubjectPrefix": "Solar Network" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "KnownProxies": ["127.0.0.1", "::1"], + "Service": { + "Name": "DysonNetwork.Ring", + "Url": "https://localhost:7259" + }, + "Etcd": { + "Insecure": true + } +} diff --git a/publish/settings/sphere.json b/publish/settings/sphere.json new file mode 100644 index 0000000..22a3a07 --- /dev/null +++ b/publish/settings/sphere.json @@ -0,0 +1,38 @@ +{ + "Debug": true, + "BaseUrl": "http://localhost:5071", + "SiteUrl": "https://solian.app", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "App": "Host=host.docker.internal;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + }, + "GeoIp": { + "DatabasePath": "./Keys/GeoLite2-City.mmdb" + }, + "RealtimeChat": { + "Endpoint": "https://solar-network-im44o8gq.livekit.cloud", + "ApiKey": "", + "ApiSecret": "" + }, + "Translation": { + "Provider": "Tencent", + "Region": "ap-hongkong", + "ProjectId": "0", + "SecretId": "", + "SecretKey": "" + }, + "KnownProxies": ["127.0.0.1", "::1"], + "Etcd": { + "Insecure": true + }, + "Service": { + "Name": "DysonNetwork.Sphere", + "Url": "https://localhost:7099" + } +} diff --git a/settings/develop.json b/settings/develop.json index b81e2d8..a70297c 100644 --- a/settings/develop.json +++ b/settings/develop.json @@ -10,12 +10,9 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + "App": "Host=host.docker.internal;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, - "KnownProxies": [ - "127.0.0.1", - "::1" - ], + "KnownProxies": ["127.0.0.1", "::1"], "Etcd": { "Insecure": true }, diff --git a/settings/drive.json b/settings/drive.json index f025412..90c148c 100644 --- a/settings/drive.json +++ b/settings/drive.json @@ -10,22 +10,19 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + "App": "Host=host.docker.internal;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Authentication": { "Schemes": { "Bearer": { - "ValidAudiences": [ - "http://localhost:5071", - "https://localhost:7099" - ], + "ValidAudiences": ["http://localhost:5071", "https://localhost:7099"], "ValidIssuer": "solar-network" } } }, "AuthToken": { - "PublicKeyPath": "Keys/PublicKey.pem", - "PrivateKeyPath": "Keys/PrivateKey.pem" + "PublicKeyPath": "app/keys/PublicKey.pem", + "PrivateKeyPath": "app/keys/PrivateKey.pem" }, "OidcProvider": { "IssuerUri": "https://nt.solian.app", @@ -122,10 +119,7 @@ } } }, - "KnownProxies": [ - "127.0.0.1", - "::1" - ], + "KnownProxies": ["127.0.0.1", "::1"], "Service": { "Name": "DysonNetwork.Drive", "Url": "https://localhost:7092" diff --git a/settings/pass.json b/settings/pass.json index 066d1a3..dea5e59 100644 --- a/settings/pass.json +++ b/settings/pass.json @@ -10,15 +10,12 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + "App": "Host=host.docker.internal;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Authentication": { "Schemes": { "Bearer": { - "ValidAudiences": [ - "http://localhost:5071", - "https://localhost:7099" - ], + "ValidAudiences": ["http://localhost:5071", "https://localhost:7099"], "ValidIssuer": "solar-network" } } @@ -74,10 +71,7 @@ } } }, - "KnownProxies": [ - "127.0.0.1", - "::1" - ], + "KnownProxies": ["127.0.0.1", "::1"], "Service": { "Name": "DysonNetwork.Pass", "Url": "https://localhost:7058" diff --git a/settings/ring.json b/settings/ring.json index 2ab4e9e..58332b9 100644 --- a/settings/ring.json +++ b/settings/ring.json @@ -9,12 +9,12 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + "App": "Host=host.docker.internal;Port=5432;Database=dyson_pusher;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "Notifications": { "Push": { "Production": true, - "Google": "./Keys/Solian.json", + "Google": "/app/keys/Solian.json", "Apple": { "PrivateKey": "./Keys/Solian.p8", "PrivateKeyId": "4US4KSX4W6", @@ -34,12 +34,9 @@ "SubjectPrefix": "Solar Network" }, "GeoIp": { - "DatabasePath": "./Keys/GeoLite2-City.mmdb" + "DatabasePath": "/app/keys/GeoLite2-City.mmdb" }, - "KnownProxies": [ - "127.0.0.1", - "::1" - ], + "KnownProxies": ["127.0.0.1", "::1"], "Service": { "Name": "DysonNetwork.Ring", "Url": "https://localhost:7259" diff --git a/settings/sphere.json b/settings/sphere.json index 1157d91..18055c5 100644 --- a/settings/sphere.json +++ b/settings/sphere.json @@ -10,10 +10,10 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "App": "Host=localhost;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" + "App": "Host=host.docker.internal;Port=5432;Database=dyson_network;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60" }, "GeoIp": { - "DatabasePath": "./Keys/GeoLite2-City.mmdb" + "DatabasePath": "/app/keys/GeoLite2-City.mmdb" }, "RealtimeChat": { "Endpoint": "https://solar-network-im44o8gq.livekit.cloud", @@ -27,10 +27,7 @@ "SecretId": "", "SecretKey": "" }, - "KnownProxies": [ - "127.0.0.1", - "::1" - ], + "KnownProxies": ["127.0.0.1", "::1"], "Etcd": { "Insecure": true }, From f6f0703cb3c9634f22b5b1f89e76eec2fa65e3f6 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 18 Sep 2025 01:02:25 +0800 Subject: [PATCH 11/11] :sparkles: Proper gRPC protocol --- DysonNetwork.Control/AppHost.cs | 20 ++- DysonNetwork.Shared/Auth/Startup.cs | 11 +- .../Http/KestrelConfiguration.cs | 45 +++++++ .../Registry/ServiceInjectionHelper.cs | 120 +++++++++++------- docker-compose.yaml | 25 +++- publish/docker-compose.yaml | 10 ++ 6 files changed, 167 insertions(+), 64 deletions(-) diff --git a/DysonNetwork.Control/AppHost.cs b/DysonNetwork.Control/AppHost.cs index 7a64e5c..ee70630 100644 --- a/DysonNetwork.Control/AppHost.cs +++ b/DysonNetwork.Control/AppHost.cs @@ -9,25 +9,35 @@ var cache = builder.AddRedis("cache"); var queue = builder.AddNats("queue").WithJetStream(); var ringService = builder.AddProject("ring") - .WithReference(queue); + .WithReference(queue) + .WithHttpHealthCheck() + .WithEndpoint(5001, 5001, "https", name: "grpc"); var passService = builder.AddProject("pass") .WithReference(cache) .WithReference(queue) - .WithReference(ringService); + .WithReference(ringService) + .WithHttpHealthCheck() + .WithEndpoint(5001, 5001, "https", name: "grpc"); var driveService = builder.AddProject("drive") .WithReference(cache) .WithReference(queue) .WithReference(passService) - .WithReference(ringService); + .WithReference(ringService) + .WithHttpHealthCheck() + .WithEndpoint(5001, 5001, "https", name: "grpc"); var sphereService = builder.AddProject("sphere") .WithReference(cache) .WithReference(queue) .WithReference(passService) - .WithReference(ringService); + .WithReference(ringService) + .WithHttpHealthCheck() + .WithEndpoint(5001, 5001, "https", name: "grpc"); var developService = builder.AddProject("develop") .WithReference(cache) .WithReference(passService) - .WithReference(ringService); + .WithReference(ringService) + .WithHttpHealthCheck() + .WithEndpoint(5001, 5001, "https", name: "grpc"); // Extra double-ended references ringService.WithReference(passService); diff --git a/DysonNetwork.Shared/Auth/Startup.cs b/DysonNetwork.Shared/Auth/Startup.cs index 9197c34..e0caf8c 100644 --- a/DysonNetwork.Shared/Auth/Startup.cs +++ b/DysonNetwork.Shared/Auth/Startup.cs @@ -1,4 +1,5 @@ using DysonNetwork.Shared.Proto; +using DysonNetwork.Shared.Registry; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -10,15 +11,7 @@ public static class DysonAuthStartup this IServiceCollection services ) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); - - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); + services.AddAuthService(); services.AddAuthentication(options => { diff --git a/DysonNetwork.Shared/Http/KestrelConfiguration.cs b/DysonNetwork.Shared/Http/KestrelConfiguration.cs index e3f0f38..e8534aa 100644 --- a/DysonNetwork.Shared/Http/KestrelConfiguration.cs +++ b/DysonNetwork.Shared/Http/KestrelConfiguration.cs @@ -20,8 +20,53 @@ public static class KestrelConfiguration builder.WebHost.ConfigureKestrel(options => { options.Limits.MaxRequestBodySize = maxRequestBodySize; + + // gRPC + options.ListenAnyIP(5001, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http2; + + var selfSignedCert = _CreateSelfSignedCertificate(); + listenOptions.UseHttps(selfSignedCert); + }); + + var httpPorts = configuration.GetValue("HTTP_PORTS", "5000") + .Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(p => int.Parse(p.Trim())) + .ToArray(); + + // Regular HTTP + foreach (var httpPort in httpPorts) + options.ListenAnyIP(httpPort, + listenOptions => { listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); }); return builder; } + + static X509Certificate2 _CreateSelfSignedCertificate() + { + using var rsa = RSA.Create(2048); + var certRequest = new CertificateRequest( + "CN=dyson.network", // Common Name for the certificate + rsa, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + + // Add extensions (e.g., for server authentication) + certRequest.CertificateExtensions.Add( + new X509EnhancedKeyUsageExtension( + new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, // Server Authentication + false)); + + // Set validity period (e.g., 1 year) + var notBefore = DateTimeOffset.UtcNow.AddDays(-1); + var notAfter = notBefore.AddYears(1); + + var certificate = certRequest.CreateSelfSigned(notBefore, notAfter); + + // Export to PKCS#12 and load using X509CertificateLoader + var pfxBytes = certificate.Export(X509ContentType.Pfx); + return X509CertificateLoader.LoadPkcs12(pfxBytes, password: null); + } } \ No newline at end of file diff --git a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs index 739d434..d948adf 100644 --- a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs +++ b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs @@ -8,72 +8,94 @@ public static class ServiceInjectionHelper { public static IServiceCollection AddRingService(this IServiceCollection services) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://ring"); - }); - + services + .AddGrpcClient(o => o.Address = new Uri("https://_grpc.ring")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + return services; } - + + public static IServiceCollection AddAuthService(this IServiceCollection services) + { + services + .AddGrpcClient(o => o.Address = new Uri("https://_grpc.pass")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + + services + .AddGrpcClient(o => o.Address = new Uri("https://_grpc.pass")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + + return services; + } + public static IServiceCollection AddAccountService(this IServiceCollection services) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); + services + .AddGrpcClient(o => o.Address = new Uri("https://_grpc.pass") ) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); services.AddSingleton(); - - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); - - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); - - services.AddGrpcClient(o => - { - o.Address = new Uri("https://pass"); - }); - + + services + .AddGrpcClient(o => + o.Address = new Uri("https://_grpc.pass") + ) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.pass")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.pass")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + return services; } - + public static IServiceCollection AddDriveService(this IServiceCollection services) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://drive"); - }); - - services.AddGrpcClient(o => - { - o.Address = new Uri("https://drive"); - }); - + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.drive")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.drive")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + return services; } - + public static IServiceCollection AddPublisherService(this IServiceCollection services) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://sphere"); - }); - + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.sphere")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + return services; } public static IServiceCollection AddDevelopService(this IServiceCollection services) { - services.AddGrpcClient(o => - { - o.Address = new Uri("https://develop"); - }); - + services.AddGrpcClient(o => o.Address = new Uri("https://_grpc.develop")) + .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler() + { ServerCertificateCustomValidationCallback = (_, _, _, _) => true } + ); + return services; } - } \ No newline at end of file +} \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 45745be..a4aa669 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -40,9 +40,11 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${RING_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" - OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire:18889" + services__pass__grpc__0: "https://pass:5001" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "ring" volumes: @@ -50,6 +52,7 @@ services: - "./settings/ring.json:/app/appsettings.json" expose: - "${RING_PORT}" + - "5001" networks: - "aspire" pass: @@ -60,9 +63,11 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${PASS_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__ring__http__0: "http://ring:${RING_PORT}" + services__ring__grpc__0: "https://ring:5001" OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "pass" @@ -71,6 +76,7 @@ services: - "./settings/pass.json:/app/appsettings.json" expose: - "${PASS_PORT}" + - "5001" networks: - "aspire" drive: @@ -81,15 +87,19 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${DRIVE_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" + services__pass__grpc__0: "https://pass:5001" services__ring__http__0: "http://ring:${RING_PORT}" + services__ring__grpc__0: "https://ring:5001" OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "drive" expose: - "${DRIVE_PORT}" + - "5001" volumes: - "./settings/drive.json:/app/appsettings.json" networks: @@ -102,10 +112,13 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${SPHERE_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" + services__pass__grpc__0: "https://pass:5001" services__ring__http__0: "http://ring:${RING_PORT}" + services__ring__grpc__0: "https://ring:5001" OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "sphere" @@ -114,6 +127,7 @@ services: - "./settings/sphere.json:/app/appsettings.json" expose: - "${SPHERE_PORT}" + - "5001" networks: - "aspire" develop: @@ -124,14 +138,18 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${DEVELOP_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" services__pass__http__0: "http://pass:${PASS_PORT}" + services__pass__grpc__0: "https://pass:5001" services__ring__http__0: "http://ring:${RING_PORT}" + services__ring__grpc__0: "https://ring:5001" OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "develop" expose: - "${DEVELOP_PORT}" + - "5001" networks: - "aspire" gateway: @@ -143,6 +161,7 @@ services: environment: ASPNETCORE_ENVIRONMENT: "Production" services__ring__http__0: "http://ring:${RING_PORT}" + services__ring__grpc__0: "https://ring:5001" REVERSEPROXY__ROUTES__route0__MATCH__PATH: "/ws" REVERSEPROXY__ROUTES__route0__CLUSTERID: "cluster_ring" REVERSEPROXY__ROUTES__route1__MATCH__PATH: "/ring/{**catch-all}" @@ -177,9 +196,13 @@ services: REVERSEPROXY__CLUSTERS__cluster_sphere__DESTINATIONS__destination1__ADDRESS: "http://_http.sphere" REVERSEPROXY__CLUSTERS__cluster_develop__DESTINATIONS__destination1__ADDRESS: "http://_http.develop" services__pass__http__0: "http://pass:${PASS_PORT}" + services__pass__grpc__0: "https://pass:5001" services__drive__http__0: "http://drive:${DRIVE_PORT}" + services__drive__grpc__0: "https://drive:5001" services__sphere__http__0: "http://sphere:${SPHERE_PORT}" + services__sphere__grpc__0: "https://sphere:5001" services__develop__http__0: "http://develop:${DEVELOP_PORT}" + services__develop__grpc__0: "https://develop:5001" OTEL_EXPORTER_OTLP_ENDPOINT: "http://aspire-dashboard:18889" OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" OTEL_SERVICE_NAME: "gateway" diff --git a/publish/docker-compose.yaml b/publish/docker-compose.yaml index d439bed..8b4f695 100644 --- a/publish/docker-compose.yaml +++ b/publish/docker-compose.yaml @@ -40,6 +40,7 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${RING_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" OTEL_EXPORTER_OTLP_ENDPOINT: "http://docker-compose-dashboard:18889" @@ -47,6 +48,7 @@ services: OTEL_SERVICE_NAME: "ring" expose: - "${RING_PORT}" + - "5001" networks: - "aspire" pass: @@ -57,6 +59,7 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${PASS_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__ring__http__0: "http://ring:${RING_PORT}" @@ -65,6 +68,7 @@ services: OTEL_SERVICE_NAME: "pass" expose: - "${PASS_PORT}" + - "5001" networks: - "aspire" drive: @@ -75,6 +79,7 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${DRIVE_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" @@ -84,6 +89,7 @@ services: OTEL_SERVICE_NAME: "drive" expose: - "${DRIVE_PORT}" + - "5001" networks: - "aspire" sphere: @@ -94,6 +100,7 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${SPHERE_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" ConnectionStrings__queue: "nats://nats:${QUEUE_PASSWORD}@queue:4222" services__pass__http__0: "http://pass:${PASS_PORT}" @@ -103,6 +110,7 @@ services: OTEL_SERVICE_NAME: "sphere" expose: - "${SPHERE_PORT}" + - "5001" networks: - "aspire" develop: @@ -113,6 +121,7 @@ services: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" HTTP_PORTS: "${DEVELOP_PORT}" + HTTPS_PORTS: "5001" ConnectionStrings__cache: "cache:6379,password=${CACHE_PASSWORD}" services__pass__http__0: "http://pass:${PASS_PORT}" services__ring__http__0: "http://ring:${RING_PORT}" @@ -121,6 +130,7 @@ services: OTEL_SERVICE_NAME: "develop" expose: - "${DEVELOP_PORT}" + - "5001" networks: - "aspire" gateway: