using System.Reflection; using Discord.Commands; using Discord.WebSocket; using FluentValidation; using Managing.Application; using Managing.Application.Abstractions; using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Services; using Managing.Application.Accounts; using Managing.Application.Agents; using Managing.Application.Backtests; using Managing.Application.ManageBot; using Managing.Application.ManageBot.Commands; using Managing.Application.MoneyManagements; using Managing.Application.Scenarios; using Managing.Application.Shared; using Managing.Application.Shared.Behaviours; using Managing.Application.Synth; using Managing.Application.Trading; using Managing.Application.Trading.Commands; using Managing.Application.Trading.Handlers; using Managing.Application.Users; using Managing.Application.Workers; using Managing.Domain.Trades; using Managing.Infrastructure.Database.PostgreSql; using Managing.Infrastructure.Databases; using Managing.Infrastructure.Databases.InfluxDb; using Managing.Infrastructure.Databases.InfluxDb.Abstractions; using Managing.Infrastructure.Databases.InfluxDb.Models; using Managing.Infrastructure.Databases.PostgreSql; using Managing.Infrastructure.Databases.PostgreSql.Configurations; using Managing.Infrastructure.Evm; using Managing.Infrastructure.Evm.Abstractions; using Managing.Infrastructure.Evm.Models.Privy; using Managing.Infrastructure.Evm.Services; using Managing.Infrastructure.Evm.Subgraphs; using Managing.Infrastructure.Exchanges; using Managing.Infrastructure.Exchanges.Abstractions; using Managing.Infrastructure.Exchanges.Exchanges; using Managing.Infrastructure.Messengers.Discord; using Managing.Infrastructure.Storage; using MediatR; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Orleans.Configuration; namespace Managing.Bootstrap; public static class ApiBootstrap { private static readonly Assembly ApplicationAssembly = typeof(StartBotCommand).GetTypeInfo().Assembly; public static IServiceCollection RegisterApiDependencies(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration.GetSection("Web3Proxy")); return services .AddApplication() .AddInfrastructure(configuration) .AddWorkers(configuration) .AddFluentValidation() .AddMediatR() ; } // Note: IClusterClient is automatically available in co-hosting scenarios // through IGrainFactory. Services should inject IGrainFactory instead of IClusterClient // to avoid circular dependency issues during DI container construction. public static IHostBuilder ConfigureOrleans(this IHostBuilder hostBuilder, IConfiguration configuration, bool isProduction) { var postgreSqlConnectionString = configuration.GetSection("PostgreSql")["Orleans"]; return hostBuilder.UseOrleans(siloBuilder => { // Configure clustering with improved networking siloBuilder .UseAdoNetClustering(options => { options.ConnectionString = postgreSqlConnectionString; options.Invariant = "Npgsql"; }) .UseAdoNetReminderService(options => { options.ConnectionString = postgreSqlConnectionString; options.Invariant = "Npgsql"; }); // Configure networking for better silo communication siloBuilder .ConfigureEndpoints(siloPort: 11111, gatewayPort: 30000) .Configure(options => { // Configure cluster options with unique identifiers options.ServiceId = "ManagingApp"; options.ClusterId = configuration["ASPNETCORE_ENVIRONMENT"] ?? "Development"; }) .Configure(options => { // Configure grain collection to prevent memory issues options.CollectionAge = TimeSpan.FromMinutes(10); options.CollectionQuantum = TimeSpan.FromMinutes(1); }) .Configure(options => { // Configure messaging for better reliability options.ResponseTimeout = TimeSpan.FromSeconds(30); }); siloBuilder .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Information)); // Only enable dashboard in development to avoid shutdown issues if (!isProduction) { siloBuilder.UseDashboard(options => { // Configure dashboard with proper shutdown handling options.Port = 9999; options.HostSelf = true; options.CounterUpdateIntervalMs = 10000; // 10 seconds options.HideTrace = true; // Hide trace to reduce dashboard overhead options.Host = "0.0.0.0"; // Allow external connections options.Username = "admin"; options.Password = "admin"; }); } siloBuilder .AddAdoNetGrainStorage("bot-store", options => { options.ConnectionString = postgreSqlConnectionString; options.Invariant = "Npgsql"; }) .AddAdoNetGrainStorage("registry-store", options => { options.ConnectionString = postgreSqlConnectionString; options.Invariant = "Npgsql"; }) .AddAdoNetGrainStorage("agent-store", options => { options.ConnectionString = postgreSqlConnectionString; options.Invariant = "Npgsql"; }) .ConfigureServices(services => { // Register existing services for Orleans DI // These will be available to grains through dependency injection services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); }); }) ; } private static IServiceCollection AddApplication(this IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddTransient, OpenPositionCommandHandler>(); services.AddTransient, ClosePositionCommandHandler>(); // Processors services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddSingleton(); services.AddSingleton(); return services; } private static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) { // Database services.AddSingleton(sp => sp.GetRequiredService>().Value); services.AddSingleton(sp => sp.GetRequiredService>().Value); services.AddSingleton(sp => sp.GetRequiredService>().Value); services.Configure(configuration.GetSection("Kaigen")); // Evm services.AddGbcFeed(); services.AddUniswapV2(); services.AddChainlink(); services.AddChainlinkGmx(); services.AddSingleton(); // PostgreSql Repositories services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); // InfluxDb Repositories services.AddTransient(); services.AddTransient(); services.AddTransient(); // Cache services.AddDistributedMemoryCache(); services.AddTransient(); services.AddSingleton(); // Services services.AddTransient(); services.AddSingleton(); return services; } private static IServiceCollection AddWorkers(this IServiceCollection services, IConfiguration configuration) { if (configuration.GetValue("WorkerBalancesTracking", false)) { services.AddHostedService(); } if (configuration.GetValue("WorkerNotifyBundleBacktest", false)) { services.AddHostedService(); } return services; } private static IServiceCollection AddMediatR(this IServiceCollection services) { return services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies())) .AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)) .AddTransient(typeof(IUnhandledExceptionBehaviour<,>), typeof(UnhandledExceptionBehaviour<,>)); } private static IServiceCollection AddFluentValidation(this IServiceCollection services) { return services.AddValidatorsFromAssembly(ApplicationAssembly); } public static IWebHostBuilder SetupDiscordBot(this IWebHostBuilder builder) { return builder.ConfigureServices(services => { services .AddSingleton() .AddSingleton() .AddSingleton() .AddHostedService(); }); } }