New service discovery system

This commit is contained in:
2025-12-13 14:23:28 +08:00
parent 8642737a07
commit bc3d030a1e
43 changed files with 142 additions and 563 deletions

View File

@@ -1,77 +0,0 @@
using Microsoft.Extensions.Hosting;
var builder = DistributedApplication.CreateBuilder(args);
var isDev = builder.Environment.IsDevelopment();
var cache = builder.AddRedis("cache");
var queue = builder.AddNats("queue").WithJetStream();
var ringService = builder.AddProject<Projects.DysonNetwork_Ring>("ring");
var passService = builder.AddProject<Projects.DysonNetwork_Pass>("pass")
.WithReference(ringService);
var driveService = builder.AddProject<Projects.DysonNetwork_Drive>("drive")
.WithReference(passService)
.WithReference(ringService);
var sphereService = builder.AddProject<Projects.DysonNetwork_Sphere>("sphere")
.WithReference(passService)
.WithReference(ringService)
.WithReference(driveService);
var developService = builder.AddProject<Projects.DysonNetwork_Develop>("develop")
.WithReference(passService)
.WithReference(ringService)
.WithReference(sphereService);
var insightService = builder.AddProject<Projects.DysonNetwork_Insight>("insight")
.WithReference(passService)
.WithReference(ringService)
.WithReference(sphereService)
.WithReference(developService);
var zoneService = builder.AddProject<Projects.DysonNetwork_Zone>("zone")
.WithReference(passService)
.WithReference(ringService)
.WithReference(sphereService)
.WithReference(developService)
.WithReference(insightService);
passService.WithReference(developService).WithReference(driveService);
List<IResourceBuilder<ProjectResource>> services =
[ringService, passService, driveService, sphereService, developService, insightService, zoneService];
for (var idx = 0; idx < services.Count; idx++)
{
var service = services[idx];
service.WithReference(cache).WithReference(queue);
var grpcPort = 7002 + idx;
if (isDev)
{
service.WithEnvironment("GRPC_PORT", grpcPort.ToString());
var httpPort = 8001 + idx;
service.WithEnvironment("HTTP_PORTS", httpPort.ToString());
service.WithHttpEndpoint(httpPort, targetPort: null, isProxied: false, name: "http");
}
else
{
service.WithHttpEndpoint(8080, targetPort: null, isProxied: false, name: "http");
}
service.WithEndpoint(isDev ? grpcPort : 7001, isDev ? null : 7001, "https", name: "grpc", isProxied: false);
}
// Extra double-ended references
ringService.WithReference(passService);
var gateway = builder.AddProject<Projects.DysonNetwork_Gateway>("gateway")
.WithEnvironment("HTTP_PORTS", "5001")
.WithHttpEndpoint(port: 5001, targetPort: null, isProxied: false, name: "http");
foreach (var service in services)
gateway.WithReference(service);
builder.AddDockerComposeEnvironment("docker-compose");
builder.Build().Run();

View File

@@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="13.0.0"/>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>a68b3195-a00d-40c2-b5ed-d675356b7cde</UserSecretsId>
<RootNamespace>DysonNetwork.Control</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.0.0"/>
<PackageReference Include="Aspire.Hosting.Docker" Version="13.0.0-preview.1.25560.3"/>
<PackageReference Include="Aspire.Hosting.Nats" Version="13.0.0"/>
<PackageReference Include="Aspire.Hosting.Redis" Version="13.0.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DysonNetwork.Develop\DysonNetwork.Develop.csproj"/>
<ProjectReference Include="..\DysonNetwork.Drive\DysonNetwork.Drive.csproj"/>
<ProjectReference Include="..\DysonNetwork.Pass\DysonNetwork.Pass.csproj"/>
<ProjectReference Include="..\DysonNetwork.Ring\DysonNetwork.Ring.csproj"/>
<ProjectReference Include="..\DysonNetwork.Sphere\DysonNetwork.Sphere.csproj"/>
<ProjectReference Include="..\DysonNetwork.Gateway\DysonNetwork.Gateway.csproj"/>
<ProjectReference Include="..\DysonNetwork.Insight\DysonNetwork.Insight.csproj"/>
<ProjectReference Include="..\DysonNetwork.Zone\DysonNetwork.Zone.csproj"/>
</ItemGroup>
</Project>

View File

@@ -1,32 +0,0 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17169;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",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21260",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22052"
}
},
"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",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:22108"
}
}
}
}

View File

@@ -1,11 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"cache": "localhost:6379"
}
}

View File

@@ -1,357 +0,0 @@
{
"$schema": "https://json.schemastore.org/aspire-8.0.json",
"resources": {
"cache": {
"type": "container.v1",
"connectionString": "{cache.bindings.tcp.host}:{cache.bindings.tcp.port},password={cache-password.value}",
"image": "docker.io/library/redis:8.2",
"entrypoint": "/bin/sh",
"args": [
"-c",
"redis-server --requirepass $REDIS_PASSWORD"
],
"env": {
"REDIS_PASSWORD": "{cache-password.value}"
},
"bindings": {
"tcp": {
"scheme": "tcp",
"protocol": "tcp",
"transport": "tcp",
"targetPort": 6379
}
}
},
"queue": {
"type": "container.v1",
"connectionString": "nats://nats:{queue-password.value}@{queue.bindings.tcp.host}:{queue.bindings.tcp.port}",
"image": "docker.io/library/nats:2.11",
"args": [
"--user",
"nats",
"--pass",
"{queue-password.value}",
"-js"
],
"bindings": {
"tcp": {
"scheme": "tcp",
"protocol": "tcp",
"transport": "tcp",
"targetPort": 4222
}
}
},
"ring": {
"type": "project.v1",
"path": "../DysonNetwork.Ring/DysonNetwork.Ring.csproj",
"env": {
"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": "8001",
"HTTPS_PORTS": "{ring.bindings.grpc.targetPort}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7002",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "ring"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8001
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7002
}
}
},
"pass": {
"type": "project.v1",
"path": "../DysonNetwork.Pass/DysonNetwork.Pass.csproj",
"env": {
"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": "8002",
"HTTPS_PORTS": "{pass.bindings.grpc.targetPort}",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"services__develop__http__0": "{develop.bindings.http.url}",
"services__develop__grpc__0": "{develop.bindings.grpc.url}",
"services__drive__http__0": "{drive.bindings.http.url}",
"services__drive__grpc__0": "{drive.bindings.grpc.url}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7003",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "pass"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8002
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7003
}
}
},
"drive": {
"type": "project.v1",
"path": "../DysonNetwork.Drive/DysonNetwork.Drive.csproj",
"env": {
"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": "8003",
"HTTPS_PORTS": "{drive.bindings.grpc.targetPort}",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7004",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "drive"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8003
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7004
}
}
},
"sphere": {
"type": "project.v1",
"path": "../DysonNetwork.Sphere/DysonNetwork.Sphere.csproj",
"env": {
"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": "8004",
"HTTPS_PORTS": "{sphere.bindings.grpc.targetPort}",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"services__drive__http__0": "{drive.bindings.http.url}",
"services__drive__grpc__0": "{drive.bindings.grpc.url}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7005",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "sphere"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8004
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7005
}
}
},
"develop": {
"type": "project.v1",
"path": "../DysonNetwork.Develop/DysonNetwork.Develop.csproj",
"env": {
"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": "8005",
"HTTPS_PORTS": "{develop.bindings.grpc.targetPort}",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"services__sphere__http__0": "{sphere.bindings.http.url}",
"services__sphere__grpc__0": "{sphere.bindings.grpc.url}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7006",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "develop"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8005
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7006
}
}
},
"insight": {
"type": "project.v1",
"path": "../DysonNetwork.Insight/DysonNetwork.Insight.csproj",
"env": {
"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": "8006",
"HTTPS_PORTS": "{insight.bindings.grpc.targetPort}",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"services__sphere__http__0": "{sphere.bindings.http.url}",
"services__sphere__grpc__0": "{sphere.bindings.grpc.url}",
"services__develop__http__0": "{develop.bindings.http.url}",
"services__develop__grpc__0": "{develop.bindings.grpc.url}",
"ConnectionStrings__cache": "{cache.connectionString}",
"ConnectionStrings__queue": "{queue.connectionString}",
"GRPC_PORT": "7007",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "insight"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 8006
},
"grpc": {
"scheme": "https",
"protocol": "tcp",
"transport": "http",
"targetPort": 7007
}
}
},
"gateway": {
"type": "project.v1",
"path": "../DysonNetwork.Gateway/DysonNetwork.Gateway.csproj",
"env": {
"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": "5001",
"services__ring__http__0": "{ring.bindings.http.url}",
"services__ring__grpc__0": "{ring.bindings.grpc.url}",
"services__pass__http__0": "{pass.bindings.http.url}",
"services__pass__grpc__0": "{pass.bindings.grpc.url}",
"services__drive__http__0": "{drive.bindings.http.url}",
"services__drive__grpc__0": "{drive.bindings.grpc.url}",
"services__sphere__http__0": "{sphere.bindings.http.url}",
"services__sphere__grpc__0": "{sphere.bindings.grpc.url}",
"services__develop__http__0": "{develop.bindings.http.url}",
"services__develop__grpc__0": "{develop.bindings.grpc.url}",
"services__insight__http__0": "{insight.bindings.http.url}",
"services__insight__grpc__0": "{insight.bindings.grpc.url}",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "gateway"
},
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 5001
}
}
},
"docker-compose": {
"error": "This resource does not support generation in the manifest."
},
"cache-password": {
"type": "parameter.v0",
"value": "{cache-password.inputs.value}",
"inputs": {
"value": {
"type": "string",
"secret": true,
"default": {
"generate": {
"minLength": 22,
"special": false
}
}
}
}
},
"queue-password": {
"type": "parameter.v0",
"value": "{queue-password.inputs.value}",
"inputs": {
"value": {
"type": "string",
"secret": true,
"default": {
"generate": {
"minLength": 22,
"special": false
}
}
}
}
},
"docker-compose-dashboard": {
"type": "container.v1",
"image": "mcr.microsoft.com/dotnet/nightly/aspire-dashboard:latest",
"bindings": {
"http": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 18888
},
"otlp-grpc": {
"scheme": "http",
"protocol": "tcp",
"transport": "http",
"targetPort": 18889
}
}
}
}
}

View File

@@ -5,7 +5,7 @@ using DysonNetwork.Pass.Credit;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Http;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization;
@@ -25,7 +25,7 @@ public class AccountController(
AccountEventService events,
SocialCreditService socialCreditService,
AffiliationSpellService ars,
GeoIpService geo
GeoService geo
) : ControllerBase
{
[HttpGet("{name}")]

View File

@@ -1,10 +1,10 @@
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Account;
public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
public class ActionLogService(GeoService geo, FlushBufferService fbs)
{
public void CreateActionLog(Guid accountId, string action, Dictionary<string, object> meta)
{

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using NodaTime;
using Microsoft.EntityFrameworkCore;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Proto;
using Microsoft.Extensions.Localization;
using AccountService = DysonNetwork.Pass.Account.AccountService;
@@ -18,7 +18,7 @@ public class AuthController(
AppDatabase db,
AccountService accounts,
AuthService auth,
GeoIpService geo,
GeoService geo,
ActionLogService als,
RingService.RingServiceClient pusher,
IConfiguration configuration,

View File

@@ -3,7 +3,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using NodaTime;
@@ -16,7 +16,7 @@ public class AuthService(
IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor,
ICacheService cache,
GeoIpService geo
GeoService geo
)
{
private HttpContext HttpContext => httpContextAccessor.HttpContext!;

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using Microsoft.EntityFrameworkCore.Migrations;
using NetTopologySuite.Geometries;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@@ -23,7 +23,7 @@ using DysonNetwork.Pass.Realm;
using DysonNetwork.Pass.Safety;
using DysonNetwork.Pass.Wallet.PaymentHandlers;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Registry;
namespace DysonNetwork.Pass.Startup;
@@ -136,7 +136,7 @@ public static class ServiceCollectionExtensions
{
services.AddScoped<RazorViewRenderer>();
services.Configure<GeoIpOptions>(configuration.GetSection("GeoIP"));
services.AddScoped<GeoIpService>();
services.AddScoped<GeoService>();
services.AddScoped<EmailService>();
services.AddScoped<PermissionService>();
services.AddScoped<ActionLogService>();

View File

@@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="dotnet-etcd" Version="8.0.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.2" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="Google.Api.CommonProtos" Version="2.17.0" />

View File

@@ -1,4 +1,6 @@
using dotnet_etcd;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Configuration;
@@ -51,9 +53,12 @@ public static class Extensions
// });
builder.Services.AddSingleton<IClock>(SystemClock.Instance);
builder.Services.AddSingleton(new EtcdClient(builder.Configuration.GetConnectionString("Registrar")));
builder.Services.AddSingleton<ServiceRegistrar>();
builder.Services.AddHostedService<ServiceRegistrarHostedService>();
builder.AddNatsClient("queue");
builder.AddRedisClient("cache", configureOptions: opts => { opts.AbortOnConnectFail = false; });
builder.AddNatsClient("Queue");
builder.AddRedisClient("Cache", configureOptions: opts => { opts.AbortOnConnectFail = false; });
// Setup cache service
builder.Services.AddStackExchangeRedisCache(options =>

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Shared.GeoIp;
namespace DysonNetwork.Shared.Geometry;
public class GeoPoint
{

View File

@@ -1,14 +1,14 @@
using MaxMind.GeoIP2;
using Microsoft.Extensions.Options;
namespace DysonNetwork.Shared.GeoIp;
namespace DysonNetwork.Shared.Geometry;
public class GeoIpOptions
{
public string DatabasePath { get; set; } = null!;
}
public class GeoIpService(IOptions<GeoIpOptions> options)
public class GeoService(IOptions<GeoIpOptions> options)
{
private readonly string _databasePath = options.Value.DatabasePath;

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Proto;
using NodaTime.Serialization.Protobuf;

View File

@@ -1,7 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Proto;
using NodaTime;
using NodaTime.Serialization.Protobuf;

View File

@@ -0,0 +1,93 @@
using dotnet_etcd;
using Etcdserverpb;
using Google.Protobuf;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace DysonNetwork.Shared.Registry;
public class ServiceRegistrar(EtcdClient etcd)
{
private CancellationTokenSource? _cts;
private string? _serviceKey;
private long _leaseId;
private long _ttlSeconds;
/// <summary>
/// Register the service in etcd with a TTL lease.
/// </summary>
public async Task RegisterAsync(string serviceName, string servicePart, string instanceId, string host, int port, long ttlSeconds = 30)
{
_ttlSeconds = ttlSeconds;
_serviceKey = $"/services/{serviceName}/${servicePart}/{instanceId}";
var serviceValue = $"{host}:{port}";
// Create and store TTL lease
var leaseResp = await etcd.LeaseGrantAsync(new LeaseGrantRequest { TTL = _ttlSeconds });
_leaseId = leaseResp.ID;
await etcd.PutAsync(new PutRequest()
{
Key = ByteString.CopyFromUtf8(_serviceKey),
Value = ByteString.CopyFromUtf8(serviceValue),
Lease = _leaseId
});
// Start a background task to keep the lease alive
_cts = new CancellationTokenSource();
_ = Task.Run(async () =>
{
while (!_cts.Token.IsCancellationRequested)
{
try
{
await etcd.LeaseKeepAlive(_leaseId, _cts.Token);
await Task.Delay(TimeSpan.FromSeconds(_ttlSeconds / 2), _cts.Token);
}
catch (Exception ex)
{
Console.WriteLine($"Error keeping lease alive: {ex.Message}");
break;
}
}
}, _cts.Token);
}
public async Task DeregisterAsync()
{
await _cts?.CancelAsync();
if (_serviceKey != null)
{
try
{
await etcd.DeleteAsync(_serviceKey);
}
catch
{
// ignore delete errors
}
}
}
}
public class ServiceRegistrarHostedService(ServiceRegistrar registrar, IConfiguration config) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
var name = config["Service:Name"];
var host = config["Service:Host"];
var grpcPort = int.Parse(config["Service:GrpcPort"]!);
var httpPort = int.Parse(config["Service:HttpPort"]!);
var instanceId = config["Service:InstanceId"] ?? Guid.NewGuid().ToString("N");
await registrar.RegisterAsync(name, "grpc", instanceId, host, grpcPort);
await registrar.RegisterAsync(name, "http", instanceId, host, httpPort);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await registrar.DeregisterAsync();
}
}

View File

@@ -2,7 +2,7 @@ using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Sphere.Autocompletion;
using DysonNetwork.Sphere.Chat;
using DysonNetwork.Sphere.Chat.Realtime;
@@ -88,7 +88,7 @@ public static class ServiceCollectionExtensions
)
{
services.Configure<GeoIpOptions>(configuration.GetSection("GeoIP"));
services.AddScoped<GeoIpService>();
services.AddScoped<GeoService>();
services.AddScoped<PublisherService>();
services.AddScoped<PublisherSubscriptionService>();
services.AddScoped<TimelineService>();

View File

@@ -2,7 +2,7 @@ using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Zone.Publication;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
@@ -74,7 +74,7 @@ public static class ServiceCollectionExtensions
)
{
services.Configure<GeoIpOptions>(configuration.GetSection("GeoIP"));
services.AddScoped<GeoIpService>();
services.AddScoped<GeoService>();
services.AddScoped<PublicationSiteService>();
services.AddScoped<PublicationSiteManager>();

View File

@@ -17,8 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Drive", "Dyson
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.Gateway", "DysonNetwork.Gateway\DysonNetwork.Gateway.csproj", "{AA4D244C-6C3A-4CD0-9DA4-5CAFFBB55085}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Insight", "DysonNetwork.Insight\DysonNetwork.Insight.csproj", "{E603CDF2-8BA0-49AE-A1F9-BD2DA5CB983D}"
@@ -107,18 +105,6 @@ Global
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|x64.Build.0 = Release|Any CPU
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|x86.ActiveCfg = Release|Any CPU
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Debug|x64.Build.0 = Debug|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Debug|x86.ActiveCfg = Debug|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Debug|x86.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
{7FFED190-51C7-4302-A8B5-96C839463458}.Release|x64.ActiveCfg = Release|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Release|x64.Build.0 = Release|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Release|x86.ActiveCfg = Release|Any CPU
{7FFED190-51C7-4302-A8B5-96C839463458}.Release|x86.Build.0 = Release|Any CPU
{AA4D244C-6C3A-4CD0-9DA4-5CAFFBB55085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA4D244C-6C3A-4CD0-9DA4-5CAFFBB55085}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA4D244C-6C3A-4CD0-9DA4-5CAFFBB55085}.Debug|x64.ActiveCfg = Debug|Any CPU

View File

@@ -52,6 +52,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fadcd336f9cde4e71998a851d7eb945bb87e00_003F0c_003F96dc130e_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnums_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5aa524c330cf4033930e4a8661c62bc331a00_003F9e_003F4e134017_003FEnums_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEtcdClient_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F866376757aa64634b820c41d3553727886400_003Ffc_003Fcfe5542c_003FEtcdClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb057cac061449bbbf3252d172d0302a2ce00_003Fa2_003Ff5ca0ddf_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F109293935a4844d5aa1610150b96edcde55000_003Fb7_003F8b7e5594_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>