using Fleet.Server; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MoShiro.Backend.Api; using MoShiro.Backend.Application; using MoShiro.Backend.Gateway; using MoShiro.Backend.Storage.Sqlite; namespace MoShiro.Backend; /// /// 组合根:在这里显式注册所有层。配置来自 appsettings.json、环境变量(MoShiro__DataDirectory 等)和命令行。 /// 测试用 在真实端口上启动整个后端。 /// public static class BackendHost { public const string ConfigurationSection = "MoShiro"; public const int MaxRequestBodyBytes = 64 * 1024; public static WebApplication Build(string[] args, Action? configureOptions = null, TimeProvider? time = null) { WebApplicationBuilder builder = WebApplication.CreateSlimBuilder(args); var options = new BackendOptions(); builder.Configuration.GetSection(ConfigurationSection).Bind(options); configureOptions?.Invoke(options); builder.WebHost.ConfigureKestrel(kestrel => kestrel.Limits.MaxRequestBodySize = MaxRequestBodyBytes); builder.Logging.AddSimpleConsole(console => { console.SingleLine = true; console.UseUtcTimestamp = true; console.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ "; }); IServiceCollection services = builder.Services; services.AddSingleton(time ?? TimeProvider.System); services.AddMoShiroSqlite(options.DataDirectory); // 国家和运营商:Backend 用节点的来源地址查一次(Probe、Agent 不访问任何外部服务);地址留空就不查 if (string.IsNullOrWhiteSpace(options.GeoLookupUrl)) { services.AddSingleton(); } else { services.AddSingleton(new HttpGeoLookup(options.GeoLookupUrl)); } services.AddMoShiroApplication(options); services.AddMoShiroGateway(); services.AddMoShiroApi(); WebApplication app = builder.Build(); // 静默死亡的 Agent(断电、断网)不会关闭连接:15 秒一个 Ping,20 秒没有 Pong 就关闭,这样面板上的在线状态才可信 app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(15), KeepAliveTimeout = TimeSpan.FromSeconds(20) }); app.UseMoShiroCors(options.CorsOrigins); app.UseMoShiroApi(); app.MapMoShiroApi(); app.MapFleetEnrollment("/api/agent/v1/enroll"); app.MapFleetLink("/api/agent/v1/rpc"); return app; } }