Postgres (#30)

* Add postgres

* Migrate users

* Migrate geneticRequest

* Try to fix Concurrent call

* Fix asyncawait

* Fix async and concurrent

* Migrate backtests

* Add cache for user by address

* Fix backtest migration

* Fix not open connection

* Fix backtest command error

* Fix concurrent

* Fix all concurrency

* Migrate TradingRepo

* Fix scenarios

* Migrate statistic repo

* Save botbackup

* Add settings et moneymanagement

* Add bot postgres

* fix a bit more backups

* Fix bot model

* Fix loading backup

* Remove cache market for read positions

* Add workers to postgre

* Fix workers api

* Reduce get Accounts for workers

* Migrate synth to postgre

* Fix backtest saved

* Remove mongodb

* botservice decorrelation

* Fix tradingbot scope call

* fix tradingbot

* fix concurrent

* Fix scope for genetics

* Fix account over requesting

* Fix bundle backtest worker

* fix a lot of things

* fix tab backtest

* Remove optimized moneymanagement

* Add light signal to not use User and too much property

* Make money management lighter

* insert indicators to awaitable

* Migrate add strategies to await

* Refactor scenario and indicator retrieval to use asynchronous methods throughout the application

* add more async await

* Add services

* Fix and clean

* Fix bot a bit

* Fix bot and add message for cooldown

* Remove fees

* Add script to deploy db

* Update dfeeploy script

* fix script

* Add idempotent script and backup

* finish script migration

* Fix did user and agent name on start bot
This commit is contained in:
Oda
2025-07-27 15:42:17 +02:00
committed by GitHub
parent 361bfbf6e8
commit 422fecea7b
294 changed files with 23953 additions and 7272 deletions

View File

@@ -102,13 +102,13 @@ namespace Managing.Api.Controllers
{
var user = await GetUser();
var result = await _AccountService.SwapGmxTokensAsync(
user,
name,
request.FromTicker,
request.ToTicker,
request.Amount,
request.OrderType,
request.TriggerRatio,
user,
name,
request.FromTicker,
request.ToTicker,
request.Amount,
request.OrderType,
request.TriggerRatio,
request.AllowedSlippage
);
return Ok(result);
@@ -126,11 +126,11 @@ namespace Managing.Api.Controllers
{
var user = await GetUser();
var result = await _AccountService.SendTokenAsync(
user,
name,
request.RecipientAddress,
request.Ticker,
request.Amount,
user,
name,
request.RecipientAddress,
request.Ticker,
request.Amount,
request.ChainId
);
return Ok(result);
@@ -142,9 +142,9 @@ namespace Managing.Api.Controllers
/// <param name="name">The name of the account to delete.</param>
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
[HttpDelete]
public ActionResult DeleteAccount(string name)
public async Task<ActionResult> DeleteAccount(string name)
{
var user = GetUser().Result;
var user = await GetUser();
return Ok(_AccountService.DeleteAccount(user, name));
}
}

View File

@@ -70,7 +70,8 @@ public class BacktestController : BaseController
public async Task<ActionResult<IEnumerable<Backtest>>> Backtests()
{
var user = await GetUser();
return Ok(_backtester.GetBacktestsByUser(user));
var backtests = await _backtester.GetBacktestsByUserAsync(user);
return Ok(backtests);
}
/// <summary>
@@ -84,7 +85,7 @@ public class BacktestController : BaseController
public async Task<ActionResult<Backtest>> Backtest(string id)
{
var user = await GetUser();
var backtest = _backtester.GetBacktestByIdForUser(user, id);
var backtest = await _backtester.GetBacktestByIdForUserAsync(user, id);
if (backtest == null)
{
@@ -103,7 +104,8 @@ public class BacktestController : BaseController
public async Task<ActionResult> DeleteBacktest(string id)
{
var user = await GetUser();
return Ok(_backtester.DeleteBacktestByUser(user, id));
var result = await _backtester.DeleteBacktestByUserAsync(user, id);
return Ok(result);
}
/// <summary>
@@ -115,7 +117,7 @@ public class BacktestController : BaseController
public async Task<ActionResult> DeleteBacktests([FromBody] DeleteBacktestsRequest request)
{
var user = await GetUser();
return Ok(_backtester.DeleteBacktestsByIdsForUser(user, request.BacktestIds));
return Ok(await _backtester.DeleteBacktestsByIdsForUserAsync(user, request.BacktestIds));
}
/// <summary>
@@ -133,7 +135,7 @@ public class BacktestController : BaseController
return BadRequest("Request ID is required");
}
var backtests = _backtester.GetBacktestsByRequestId(requestId);
var backtests = await _backtester.GetBacktestsByRequestIdAsync(requestId);
return Ok(backtests);
}
@@ -177,7 +179,7 @@ public class BacktestController : BaseController
}
var (backtests, totalCount) =
_backtester.GetBacktestsByRequestIdPaginated(requestId, page, pageSize, sortBy, sortOrder);
await _backtester.GetBacktestsByRequestIdPaginatedAsync(requestId, page, pageSize, sortBy, sortOrder);
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
@@ -243,7 +245,7 @@ public class BacktestController : BaseController
return BadRequest("Sort order must be 'asc' or 'desc'");
}
var (backtests, totalCount) = _backtester.GetBacktestsByUserPaginated(user, page, pageSize, sortBy, sortOrder);
var (backtests, totalCount) = await _backtester.GetBacktestsByUserPaginatedAsync(user, page, pageSize, sortBy, sortOrder);
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var response = new PaginatedBacktestsResponse
@@ -528,7 +530,7 @@ public class BacktestController : BaseController
_backtester.DeleteBundleBacktestRequestByIdForUser(user, id);
// Then, delete all related backtests
var backtestsDeleted = _backtester.DeleteBacktestsByRequestId(id);
var backtestsDeleted = await _backtester.DeleteBacktestsByRequestIdAsync(id);
return Ok(new
{
@@ -693,7 +695,7 @@ public class BacktestController : BaseController
_geneticService.DeleteGeneticRequestByIdForUser(user, id);
// Then, delete all related backtests
var backtestsDeleted = _backtester.DeleteBacktestsByRequestId(id);
var backtestsDeleted = await _backtester.DeleteBacktestsByRequestIdAsync(id);
return Ok(new
{

View File

@@ -22,17 +22,16 @@ public abstract class BaseController : ControllerBase
var identity = HttpContext?.User.Identity as ClaimsIdentity;
if (identity != null)
{
var address = identity.Claims.FirstOrDefault(c => c.Type == "address").Value;
var user = await _userService.GetUserByAddressAsync(address);
if (user != null)
var address = identity.Claims.FirstOrDefault(c => c.Type == "address")?.Value;
if (address != null)
{
var user = await _userService.GetUserByAddressAsync(address);
return user;
}
throw new Exception("User not found for this token");
}
throw new Exception("Not identity assigned to this token");
}
}

View File

@@ -128,8 +128,14 @@ public class BotController : BaseController
var user = await GetUser();
if (string.IsNullOrEmpty(user.AgentName))
{
return BadRequest(
"Agent name is required to start a bot. Please configure your agent name in the user profile.");
}
// Get money management - either by name lookup or use provided object
MoneyManagement moneyManagement;
LightMoneyManagement moneyManagement;
if (!string.IsNullOrEmpty(request.Config.MoneyManagementName))
{
moneyManagement =
@@ -144,12 +150,6 @@ public class BotController : BaseController
moneyManagement = Map(request.Config.MoneyManagement);
// Format percentage values if using custom money management
moneyManagement?.FormatPercentage();
// Ensure user is set for custom money management
if (moneyManagement != null)
{
moneyManagement.User = user;
}
}
// Validate initialTradingBalance
@@ -425,7 +425,7 @@ public class BotController : BaseController
new StopBotCommand(bot.Identifier));
// Get the saved bot backup
var backup = _botService.GetBotBackup(bot.Identifier);
var backup = await _botService.GetBotBackup(bot.Identifier);
if (backup != null)
{
_botService.StartBotFromBackup(backup);
@@ -564,7 +564,8 @@ public class BotController : BaseController
catch (Exception ex)
{
_logger.LogError(ex, "Error opening position manually");
return StatusCode(500, $"Error opening position: {ex.Message}");
return StatusCode(500,
$"Error opening position: {ex.Message}, {ex.InnerException?.Message} or {ex.StackTrace}");
}
}
@@ -699,20 +700,31 @@ public class BotController : BaseController
}
// Validate and get the money management
MoneyManagement moneyManagement = null;
LightMoneyManagement moneyManagement = null;
if (!string.IsNullOrEmpty(request.MoneyManagementName))
{
// Load money management by name
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
if (moneyManagement == null)
var fullMoneyManagement =
await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
if (fullMoneyManagement == null)
{
return BadRequest($"Money management '{request.MoneyManagementName}' not found");
}
if (moneyManagement.User?.Name != user.Name)
if (fullMoneyManagement.User?.Name != user.Name)
{
return Forbid("You don't have permission to use this money management");
}
// Convert to LightMoneyManagement
moneyManagement = new LightMoneyManagement
{
Name = fullMoneyManagement.Name,
Timeframe = fullMoneyManagement.Timeframe,
StopLoss = fullMoneyManagement.StopLoss,
TakeProfit = fullMoneyManagement.TakeProfit,
Leverage = fullMoneyManagement.Leverage
};
}
else if (request.MoneyManagement != null)
{
@@ -720,9 +732,6 @@ public class BotController : BaseController
moneyManagement = request.MoneyManagement;
// Format percentage values if using custom money management
moneyManagement.FormatPercentage();
// Ensure user is set for custom money management
moneyManagement.User = user;
}
else
{

View File

@@ -206,14 +206,14 @@ public class DataController : ControllerBase
/// <returns>A <see cref="SpotlightOverview"/> object containing spotlight data.</returns>
[Authorize]
[HttpGet("Spotlight")]
public ActionResult<SpotlightOverview> GetSpotlight()
public async Task<ActionResult<SpotlightOverview>> GetSpotlight()
{
var overview = _cacheService.GetOrSave(nameof(SpotlightOverview),
() => { return _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2)); }, TimeSpan.FromMinutes(2));
if (overview?.Spotlights.Count < overview?.ScenarioCount || overview == null)
var cacheKey = $"Spotlight_{DateTime.Now.AddDays(-2).ToString("yyyy-MM-dd")}";
var overview = _cacheService.GetValue<SpotlightOverview>(cacheKey);
if (overview == null)
{
overview = _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2));
overview = await _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2));
_cacheService.SaveValue(cacheKey, overview, TimeSpan.FromMinutes(2));
}
return Ok(overview);
@@ -256,7 +256,7 @@ public class DataController : ControllerBase
{
// Map ScenarioRequest to domain Scenario object
var domainScenario = MapScenarioRequestToScenario(request.Scenario);
indicatorsValues = await _tradingService.CalculateIndicatorsValuesAsync(domainScenario, candles);
indicatorsValues = _tradingService.CalculateIndicatorsValuesAsync(domainScenario, candles);
}
return Ok(new CandlesWithIndicatorsResponse

View File

@@ -40,8 +40,16 @@ public class MoneyManagementController : BaseController
[HttpPost]
public async Task<ActionResult<MoneyManagement>> PostMoneyManagement(MoneyManagement moneyManagement)
{
var user = await GetUser();
return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(user, moneyManagement));
try
{
var user = await GetUser();
var result = await _moneyManagementService.CreateOrUpdateMoneyManagement(user, moneyManagement);
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, $"Error creating/updating money management: {ex.Message}");
}
}
/// <summary>
@@ -52,8 +60,16 @@ public class MoneyManagementController : BaseController
[Route("moneymanagements")]
public async Task<ActionResult<IEnumerable<MoneyManagement>>> GetMoneyManagements()
{
var user = await GetUser();
return Ok(_moneyManagementService.GetMoneyMangements(user));
try
{
var user = await GetUser();
var moneyManagements = await _moneyManagementService.GetMoneyMangements(user);
return Ok(moneyManagements);
}
catch (Exception ex)
{
return StatusCode(500, $"Error retrieving money managements: {ex.Message}");
}
}
/// <summary>
@@ -64,8 +80,22 @@ public class MoneyManagementController : BaseController
[HttpGet]
public async Task<ActionResult<MoneyManagement>> GetMoneyManagement(string name)
{
var user = await GetUser();
return Ok(await _moneyManagementService.GetMoneyMangement(user, name));
try
{
var user = await GetUser();
var result = await _moneyManagementService.GetMoneyMangement(user, name);
if (result == null)
{
return NotFound($"Money management strategy '{name}' not found");
}
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, $"Error retrieving money management: {ex.Message}");
}
}
/// <summary>
@@ -76,7 +106,21 @@ public class MoneyManagementController : BaseController
[HttpDelete]
public async Task<ActionResult> DeleteMoneyManagement(string name)
{
var user = await GetUser();
return Ok(_moneyManagementService.DeleteMoneyManagement(user, name));
try
{
var user = await GetUser();
var result = await _moneyManagementService.DeleteMoneyManagement(user, name);
if (!result)
{
return NotFound($"Money management strategy '{name}' not found or could not be deleted");
}
return Ok(new { success = true, message = $"Money management strategy '{name}' deleted successfully" });
}
catch (Exception ex)
{
return StatusCode(500, $"Error deleting money management: {ex.Message}");
}
}
}

View File

@@ -43,7 +43,7 @@ public class ScenarioController : BaseController
public async Task<ActionResult<IEnumerable<ScenarioViewModel>>> GetScenarios()
{
var user = await GetUser();
var scenarios = _scenarioService.GetScenariosByUser(user);
var scenarios = await _scenarioService.GetScenariosByUserAsync(user);
var scenarioViewModels = scenarios.Select(MapToScenarioViewModel);
return Ok(scenarioViewModels);
}
@@ -59,7 +59,7 @@ public class ScenarioController : BaseController
int? loopbackPeriod = null)
{
var user = await GetUser();
var scenario = _scenarioService.CreateScenarioForUser(user, name, strategies, loopbackPeriod);
var scenario = await _scenarioService.CreateScenarioForUser(user, name, strategies, loopbackPeriod);
var scenarioViewModel = MapToScenarioViewModel(scenario);
return Ok(scenarioViewModel);
}
@@ -73,7 +73,7 @@ public class ScenarioController : BaseController
public async Task<ActionResult> DeleteScenario(string name)
{
var user = await GetUser();
return Ok(_scenarioService.DeleteScenarioByUser(user, name));
return Ok(await _scenarioService.DeleteScenarioByUser(user, name));
}
// Update scenario
@@ -81,7 +81,7 @@ public class ScenarioController : BaseController
public async Task<ActionResult> UpdateScenario(string name, List<string> strategies, int? loopbackPeriod = null)
{
var user = await GetUser();
return Ok(_scenarioService.UpdateScenarioByUser(user, name, strategies, loopbackPeriod));
return Ok(await _scenarioService.UpdateScenarioByUser(user, name, strategies, loopbackPeriod));
}
/// <summary>
@@ -93,7 +93,7 @@ public class ScenarioController : BaseController
public async Task<ActionResult<IEnumerable<IndicatorViewModel>>> GetIndicators()
{
var user = await GetUser();
var indicators = _scenarioService.GetIndicatorsByUser(user);
var indicators = await _scenarioService.GetIndicatorsAsync();
var indicatorViewModels = indicators.Select(MapToIndicatorViewModel);
return Ok(indicatorViewModels);
}
@@ -127,7 +127,7 @@ public class ScenarioController : BaseController
int? cyclePeriods = null)
{
var user = await GetUser();
var indicator = _scenarioService.CreateIndicatorForUser(
var indicator = await _scenarioService.CreateIndicatorForUser(
user,
indicatorType,
name,
@@ -153,7 +153,7 @@ public class ScenarioController : BaseController
public async Task<ActionResult> DeleteIndicator(string name)
{
var user = await GetUser();
return Ok(_scenarioService.DeleteIndicatorByUser(user, name));
return Ok(await _scenarioService.DeleteIndicatorByUser(user, name));
}
// Update indicator
@@ -172,7 +172,7 @@ public class ScenarioController : BaseController
int? cyclePeriods = null)
{
var user = await GetUser();
return Ok(_scenarioService.UpdateIndicatorByUser(
return Ok(await _scenarioService.UpdateIndicatorByUser(
user,
indicatorType,
name,

View File

@@ -1,19 +1,15 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Scenarios;
using Managing.Domain.Users;
using static Managing.Common.Enums;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers;
/// <summary>
/// Controller for managing application settings and configurations.
/// Provides endpoints for setting up default configurations and resetting settings.
/// Requires authorization for access and produces JSON responses.
/// </summary>
[ApiController]
[Authorize]
[Route("[controller]")]
@@ -22,28 +18,58 @@ public class SettingsController : BaseController
{
private readonly ISettingsService _settingsService;
/// <summary>
/// Initializes a new instance of the <see cref="SettingsController"/> class.
/// </summary>
/// <param name="settingsService">The service for managing application settings.</param>
/// <param name="userService">The service for user-related operations.</param>
public SettingsController(ISettingsService settingsService, IUserService userService)
: base(userService)
{
_settingsService = settingsService;
}
/// <summary>
/// Sets up initial application settings.
/// </summary>
/// <returns>A result indicating if the setup was successful.</returns>
[HttpPost]
public ActionResult SetupSettings()
public async Task<ActionResult<bool>> SetupSettings()
{
return Ok(_settingsService.SetupSettings());
try
{
var result = await _settingsService.SetupSettings();
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, $"Error setting up settings: {ex.Message}");
}
}
/// <summary>
/// Resets all application settings to their default values.
/// </summary>
/// <returns>A result indicating if the reset was successful.</returns>
[HttpDelete]
public async Task<ActionResult<bool>> ResetSettings()
{
return Ok(await _settingsService.ResetSettings());
try
{
var result = await _settingsService.ResetSettings();
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, $"Error resetting settings: {ex.Message}");
}
}
/// <summary>
/// Creates default configuration for backtesting including Money Management, Strategy, and Scenario
/// for the authenticated user.
/// </summary>
/// <returns>A result indicating if the default configuration was created successfully</returns>
/// <returns>A result indicating if the default configuration was created successfully.</returns>
[HttpPost]
[Route("create-default-config")]
public async Task<ActionResult<bool>> CreateDefaultConfiguration()
@@ -52,9 +78,12 @@ public class SettingsController : BaseController
{
var user = await GetUser();
if (user == null)
return Unauthorized("User not found");
{
return Unauthorized("User not found or authentication failed");
}
return Ok(await _settingsService.CreateDefaultConfiguration(user));
var result = await _settingsService.CreateDefaultConfiguration(user);
return Ok(result);
}
catch (Exception ex)
{

View File

@@ -51,17 +51,6 @@ public class TradingController : BaseController
_moneyManagementService = moneyManagementService;
}
/// <summary>
/// Retrieves a list of positions based on the initiator type.
/// </summary>
/// <param name="positionInitiator">The initiator of the position (e.g., User, System).</param>
/// <returns>A list of positions.</returns>
[HttpGet("GetPositions")]
public async Task<ActionResult<List<Position>>> GetPositions(PositionInitiator positionInitiator)
{
var result = await _mediator.Send(new GetPositionsCommand(positionInitiator));
return Ok(result);
}
/// <summary>
/// Retrieves a specific trade by account name, ticker, and exchange order ID.
@@ -98,7 +87,7 @@ public class TradingController : BaseController
[HttpPost("ClosePosition")]
public async Task<ActionResult<Position>> ClosePosition(string identifier)
{
var position = _tradingService.GetPositionByIdentifier(identifier);
var position = await _tradingService.GetPositionByIdentifierAsync(identifier);
var result = await _closeTradeCommandHandler.Handle(new ClosePositionCommand(position));
return Ok(result);
}

View File

@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="8.1.0"/>
<PackageReference Include="AspNetCore.HealthChecks.Npgsql" Version="8.1.0"/>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0"/>
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="9.0.0"/>
<PackageReference Include="Essential.LoggerProvider.Elasticsearch" Version="1.3.2"/>
@@ -52,8 +52,4 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Workers\"/>
</ItemGroup>
</Project>

View File

@@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
namespace Managing.Api.Models.Responses
@@ -11,56 +10,67 @@ namespace Managing.Api.Models.Responses
/// <summary>
/// Current status of the bot (Up, Down, etc.)
/// </summary>
[Required] public string Status { get; internal set; }
[Required]
public string Status { get; internal set; }
/// <summary>
/// List of signals generated by the bot
/// </summary>
[Required] public List<Signal> Signals { get; internal set; }
[Required]
public List<LightSignal> Signals { get; internal set; }
/// <summary>
/// List of positions opened by the bot
/// </summary>
[Required] public List<Position> Positions { get; internal set; }
[Required]
public List<Position> Positions { get; internal set; }
/// <summary>
/// Candles used by the bot for analysis
/// </summary>
[Required] public List<Candle> Candles { get; internal set; }
[Required]
public List<Candle> Candles { get; internal set; }
/// <summary>
/// Current win rate percentage
/// </summary>
[Required] public int WinRate { get; internal set; }
[Required]
public int WinRate { get; internal set; }
/// <summary>
/// Current profit and loss
/// </summary>
[Required] public decimal ProfitAndLoss { get; internal set; }
[Required]
public decimal ProfitAndLoss { get; internal set; }
/// <summary>
/// Unique identifier for the bot
/// </summary>
[Required] public string Identifier { get; set; }
[Required]
public string Identifier { get; set; }
/// <summary>
/// Agent name associated with the bot
/// </summary>
[Required] public string AgentName { get; set; }
[Required]
public string AgentName { get; set; }
/// <summary>
/// The full trading bot configuration
/// </summary>
[Required] public TradingBotConfig Config { get; internal set; }
[Required]
public TradingBotConfig Config { get; internal set; }
/// <summary>
/// The time when the bot was created
/// </summary>
[Required] public DateTime CreateDate { get; internal set; }
[Required]
public DateTime CreateDate { get; internal set; }
/// <summary>
/// The time when the bot was started
/// </summary>
[Required] public DateTime StartupTime { get; internal set; }
[Required]
public DateTime StartupTime { get; internal set; }
}
}

View File

@@ -10,11 +10,12 @@ using Managing.Bootstrap;
using Managing.Common;
using Managing.Core.Middleawares;
using Managing.Infrastructure.Databases.InfluxDb.Models;
using Managing.Infrastructure.Databases.MongoDb;
using Managing.Infrastructure.Databases.MongoDb.Configurations;
using Managing.Infrastructure.Databases.PostgreSql;
using Managing.Infrastructure.Databases.PostgreSql.Configurations;
using Managing.Infrastructure.Evm.Models.Privy;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
@@ -71,7 +72,7 @@ builder.Services.AddServiceDiscovery();
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), ["api"]);
var mongoConnectionString = builder.Configuration.GetSection(Constants.Databases.MongoDb)["ConnectionString"];
var postgreSqlConnectionString = builder.Configuration.GetSection(Constants.Databases.PostgreSql)["ConnectionString"];
var influxUrl = builder.Configuration.GetSection(Constants.Databases.InfluxDb)["Url"];
var web3ProxyUrl = builder.Configuration.GetSection("Web3Proxy")["BaseUrl"];
@@ -87,9 +88,38 @@ builder.Services.AddHttpClient("GmxHealthCheck")
builder.Services.AddSingleton<Web3ProxyHealthCheck>(sp =>
new Web3ProxyHealthCheck(sp.GetRequiredService<IHttpClientFactory>(), web3ProxyUrl));
// Add PostgreSQL DbContext with improved concurrency and connection management
builder.Services.AddDbContext<ManagingDbContext>(options =>
{
options.UseNpgsql(postgreSqlConnectionString, npgsqlOptions =>
{
// Configure connection pooling and timeout settings for better concurrency
npgsqlOptions.CommandTimeout(60); // Increase command timeout for complex queries
npgsqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(10),
errorCodesToAdd: null);
});
// Enable detailed errors in development
if (builder.Environment.IsDevelopment())
{
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging();
options.EnableThreadSafetyChecks(); // Enable thread safety checks in development
}
// Configure query tracking behavior for better performance
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); // Default to no tracking for better performance
// Enable service provider caching for better performance
options.EnableServiceProviderCaching();
// Enable connection resiliency for backtest and high-load scenarios
options.LogTo(msg => Console.WriteLine(msg), LogLevel.Warning); // Log warnings for connection issues
}, ServiceLifetime.Scoped); // Explicitly specify scoped lifetime for proper request isolation
// Add specific health checks for databases and other services
builder.Services.AddHealthChecks()
.AddMongoDb(mongoConnectionString, name: "mongodb", tags: ["database"])
.AddNpgSql(postgreSqlConnectionString, name: "postgresql", tags: ["database"])
.AddUrlGroup(new Uri($"{influxUrl}/health"), name: "influxdb", tags: ["database"])
.AddCheck<Web3ProxyHealthCheck>("web3proxy", tags: ["api", "external"])
.AddCheck<CandleDataHealthCheck>("candle-data", tags: ["database", "candles"])
@@ -120,7 +150,7 @@ builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
});
builder.Services.AddOptions();
builder.Services.Configure<ManagingDatabaseSettings>(builder.Configuration.GetSection(Constants.Databases.MongoDb));
builder.Services.Configure<PostgreSqlSettings>(builder.Configuration.GetSection(Constants.Databases.PostgreSql));
builder.Services.Configure<InfluxDbSettings>(builder.Configuration.GetSection(Constants.Databases.InfluxDb));
builder.Services.Configure<PrivySettings>(builder.Configuration.GetSection(Constants.ThirdParty.Privy));
builder.Services.AddControllers().AddJsonOptions(options =>
@@ -209,25 +239,6 @@ if (builder.Configuration.GetValue<bool>("EnableBotManager", false))
// App
var app = builder.Build();
app.UseSerilogRequestLogging();
// Create MongoDB indexes on startup
try
{
var indexService = app.Services.GetRequiredService<IndexService>();
await indexService.CreateIndexesAsync();
}
catch (Exception ex)
{
// Log the error but don't fail the application startup
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Failed to create MongoDB indexes on startup. The application will continue without indexes.");
}
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseOpenApi();
app.UseSwaggerUI(c =>
{

View File

@@ -1,21 +0,0 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"System": "Error",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200/"
},
"Sentry": {
"Debug": true,
"TracesSampleRate": 1.0,
"SendDefaultPii": true,
"DiagnosticLevel": "Debug"
}
}

View File

@@ -1,8 +1,4 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb:27017",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "",

View File

@@ -1,10 +1,4 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb:27017",
"DatabaseName": "ManagingDb",
"UserName": "admin",
"Password": "!MotdepasseFort11"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "managing-org",

View File

@@ -1,13 +1,12 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://localhost:8086/",
"Organization": "managing-org",
"Token": "Fw2FPL2OwTzDHzSbR2Sd5xs0EKQYy00Q-hYKYAhr9cC1_q5YySONpxuf_Ck0PTjyUiF13xXmi__bu_pXH-H9zA=="
},
"PostgreSql": {
"ConnectionString": "Host=localhost;Port=5432;Database=managing;Username=postgres;Password=postgres"
},
"Privy": {
"AppId": "cm6f47n1l003jx7mjwaembhup",
"AppSecret": "63Chz2z5M8TgR5qc8dznSLRAGTHTyPU4cjdQobrBF1Cx5tszZpTuFgyrRd7hZ2k6HpwDz3GEwQZzsCqHb8Z311bF"
@@ -39,5 +38,6 @@
},
"AllowedHosts": "*",
"WorkerBotManager": true,
"WorkerBalancesTracking": true
"WorkerBalancesTracking": false,
"WorkerNotifyBundleBacktest": true
}

View File

@@ -1,7 +1,6 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://admin:vgRehYTdhghDR@srv-captain--mongo:27017/?authMechanism=SCRAM-SHA-256",
"DatabaseName": "ManagingDb"
"PostgreSql": {
"ConnectionString": "Host=apps.prod.live;Port=5432;Database=managing;Username=postgres;Password=postgres"
},
"InfluxDb": {
"Url": "https://influx-db.apps.managing.live",

View File

@@ -1,7 +1,6 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://admin:r8oJiDIKbsEi@srv-captain--mongo-db:27017/?authMechanism=SCRAM-SHA-256",
"DatabaseName": "ManagingDb"
"PostgreSql": {
"ConnectionString": "Host=managing-postgre.apps.managing.live;Port=5432;Database=managing;Username=postgres;Password=29032b13a5bc4d37"
},
"InfluxDb": {
"Url": "http://srv-captain--influx-db:8086/",

View File

@@ -1,8 +1,4 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://admin:r8oJiDIKbsEi@mongo-db.apps.managing.live:27017/?authMechanism=SCRAM-SHA-256",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "https://influx-db.apps.managing.live",
"Organization": "managing-org",
@@ -12,6 +8,9 @@
"AppId": "cm6f47n1l003jx7mjwaembhup",
"AppSecret": "63Chz2z5M8TgR5qc8dznSLRAGTHTyPU4cjdQobrBF1Cx5tszZpTuFgyrRd7hZ2k6HpwDz3GEwQZzsCqHb8Z311bF"
},
"PostgreSql": {
"ConnectionString": "Host=managing-postgre.apps.managing.live;Port=5432;Database=managing;Username=postgres;Password=29032b13a5bc4d37"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",

View File

@@ -12,9 +12,8 @@
"Jwt": {
"Secret": "2ed5f490-b6c1-4cad-8824-840c911f1fe6"
},
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb",
"DatabaseName": "ManagingDb"
"PostgreSql": {
"ConnectionString": "Host=localhost;Port=5432;Database=managing;Username=postgres;Password=postgres"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",