docker files fixes from liaqat

This commit is contained in:
alirehmani
2024-05-03 16:39:25 +05:00
commit 464a8730e8
587 changed files with 44288 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
using Managing.Application.Abstractions.Services;
namespace Managing.Api.Authorization;
public class JwtMiddleware
{
private readonly RequestDelegate _next;
public JwtMiddleware(RequestDelegate next, IConfiguration config)
{
_next = next;
}
public async Task Invoke(HttpContext context, IUserService userService, IJwtUtils jwtUtils)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
var userId = jwtUtils.ValidateJwtToken(token);
if (userId != null)
{
// attach user to context on successful jwt validation
context.Items["User"] = await userService.GetUserByAddressAsync(userId);
}
await _next(context);
}
}

View File

@@ -0,0 +1,70 @@
using Managing.Domain.Users;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Managing.Api.Authorization;
public interface IJwtUtils
{
public string GenerateJwtToken(User user, string publicAddress);
public string ValidateJwtToken(string token);
}
public class JwtUtils : IJwtUtils
{
private readonly string _secret;
public JwtUtils(IConfiguration config)
{
_secret = config.GetValue<string>("Jwt:Secret");
}
public string GenerateJwtToken(User user, string publicAddress)
{
// generate token that is valid for 15 minutes
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("address", publicAddress) }),
Expires = DateTime.UtcNow.AddDays(15),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
public string ValidateJwtToken(string token)
{
if (token == null || string.IsNullOrEmpty(token))
return null;
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_secret);
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
var address = jwtToken.Claims.First(x => x.Type == "address").Value;
// return user id from JWT token if validation successful
return address;
}
catch
{
// return null if validation fails
return null;
}
}
}

View File

@@ -0,0 +1,58 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers
{
[Authorize]
public class AccountController : BaseController
{
private readonly IAccountService _AccountService;
public AccountController(
IAccountService AccountService,
IUserService userService)
: base(userService)
{
_AccountService = AccountService;
}
[HttpPost]
public async Task<ActionResult<Account>> PostAccount(Account Account)
{
var user = await GetUser();
return Ok(await _AccountService.CreateAccount(user, Account));
}
[HttpGet]
[Route("accounts")]
public async Task<ActionResult<IEnumerable<Account>>> GetAccounts()
{
var user = await GetUser();
return Ok(_AccountService.GetAccountsByUser(user, true));
}
[HttpGet]
[Route("balances")]
public async Task<ActionResult<IEnumerable<Account>>> GetAccountsBalances()
{
var user = await GetUser();
return Ok(_AccountService.GetAccountsBalancesByUser(user));
}
[HttpGet]
public async Task<ActionResult<Account>> GetAccount(string name)
{
var user = await GetUser();
return Ok(await _AccountService.GetAccountByUser(user, name, true, true));
}
[HttpDelete]
public ActionResult DeleteAccount(string name)
{
var user = GetUser().Result;
return Ok(_AccountService.DeleteAccount(user, name));
}
}
}

View File

@@ -0,0 +1,133 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Domain.Backtests;
using Managing.Domain.MoneyManagements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class BacktestController : ControllerBase
{
private readonly IHubContext<BotHub> _hubContext;
private readonly IBacktester _backtester;
private readonly IScenarioService _scenarioService;
private readonly IAccountService _accountService;
private readonly IMoneyManagementService _moneyManagementService;
public BacktestController(
IHubContext<BotHub> hubContext,
IBacktester backtester,
IScenarioService scenarioService,
IAccountService accountService,
IMoneyManagementService moneyManagementService)
{
_hubContext = hubContext;
_backtester = backtester;
_scenarioService = scenarioService;
_accountService = accountService;
_moneyManagementService = moneyManagementService;
}
[HttpGet]
public ActionResult<IEnumerable<Backtest>> Backtests()
{
return Ok(_backtester.GetBacktests());
}
[HttpDelete]
public ActionResult DeleteBacktest(string id)
{
return Ok(_backtester.DeleteBacktest(id));
}
[HttpDelete]
[Route("deleteAll")]
public ActionResult DeleteBacktests()
{
return Ok(_backtester.DeleteBacktests());
}
[HttpPost]
[Route("Run")]
public async Task<ActionResult<Backtest>> Run(string accountName,
BotType botType,
Ticker ticker,
string scenarioName,
Timeframe timeframe,
bool watchOnly,
int days,
decimal balance,
string moneyManagementName,
MoneyManagement? moneyManagement = null,
bool save = false)
{
if (string.IsNullOrEmpty(accountName))
{
throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
}
if (string.IsNullOrEmpty(scenarioName))
{
throw new ArgumentException($"'{nameof(scenarioName)}' cannot be null or empty.", nameof(scenarioName));
}
if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
{
throw new ArgumentException($"'{nameof(moneyManagementName)}' and '{nameof(moneyManagement)}' cannot be null or empty.", nameof(moneyManagementName));
}
if (days > 0)
{
days = days * -1;
}
Backtest backtestResult = null;
var scenario = _scenarioService.GetScenario(scenarioName);
var account = await _accountService.GetAccount(accountName, true, false);
if (!string.IsNullOrEmpty(moneyManagementName) && moneyManagement is null)
{
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
}
else
{
moneyManagement.FormatPercentage();
}
if (scenario == null)
return BadRequest("No scenario found");
switch (botType)
{
case BotType.SimpleBot:
break;
case BotType.ScalpingBot:
backtestResult = _backtester.RunScalpingBotBacktest(account, moneyManagement, ticker, scenario,
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
break;
case BotType.FlippingBot:
backtestResult = _backtester.RunFlippingBotBacktest(account, moneyManagement, ticker, scenario,
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
break;
}
await NotifyBacktesingSubscriberAsync(backtestResult);
return Ok(backtestResult);
}
private async Task NotifyBacktesingSubscriberAsync(Backtest backtesting)
{
if(backtesting != null){
await _hubContext.Clients.All.SendAsync("BacktestsSubscription", backtesting);
}
}
}

View File

@@ -0,0 +1,35 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Users;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace Managing.Api.Controllers;
[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public abstract class BaseController : ControllerBase
{
private readonly IUserService _userService;
public BaseController(IUserService userService)
{
_userService = userService;
}
protected async Task<User> GetUser()
{
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)
return user;
throw new Exception("User not found for this token");
}
throw new Exception("Not identity assigned to this token");
}
}

View File

@@ -0,0 +1,168 @@
using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class BotController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<BotController> _logger;
private readonly IHubContext<BotHub> _hubContext;
private readonly IBacktester _backtester;
public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext, IBacktester backtester)
{
_logger = logger;
_mediator = mediator;
_hubContext = hubContext;
_backtester = backtester;
}
[HttpPost]
[Route("Start")]
public async Task<ActionResult<string>> Start(StartBotRequest request)
{
var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName, request.IsForWatchOnly));
await NotifyBotSubscriberAsync();
return Ok(result);
}
[HttpGet]
[Route("Stop")]
public async Task<ActionResult<string>> Stop(BotType botType, string botName)
{
var result = await _mediator.Send(new StopBotCommand(botType, botName));
_logger.LogInformation($"{botType} type called {botName} is now {result}");
await NotifyBotSubscriberAsync();
return Ok(result);
}
[HttpDelete]
[Route("Delete")]
public async Task<ActionResult<bool>> Delete(string botName)
{
var result = await _mediator.Send(new DeleteBotCommand(botName));
_logger.LogInformation($"{botName} is now deleted");
await NotifyBotSubscriberAsync();
return Ok(result);
}
[HttpGet]
[Route("StopAll")]
public async Task<string> StopAll()
{
var bots = await GetBotList();
var result = "";
foreach (var bot in bots)
{
result += $"{bot.Name} : ";
result = await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
result += $" |";
}
await NotifyBotSubscriberAsync();
return result;
}
[HttpGet]
[Route("Restart")]
public async Task<ActionResult<string>> Restart(BotType botType, string botName)
{
var result = await _mediator.Send(new RestartBotCommand(botType, botName));
_logger.LogInformation($"{botType} type called {botName} is now {result}");
await NotifyBotSubscriberAsync();
return Ok(result);
}
[HttpGet]
[Route("RestartAll")]
public async Task<string> RestartAll()
{
var bots = await GetBotList();
var result = "";
foreach (var bot in bots)
{
result += $"{bot.Name} : ";
result = await _mediator.Send(new RestartBotCommand(bot.BotType, bot.Name));
result += $" |";
}
await NotifyBotSubscriberAsync();
return result;
}
[HttpGet]
[Route("ToggleIsForWatching")]
public async Task<ActionResult<string>> ToggleIsForWatching(string botName)
{
var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
_logger.LogInformation($"{botName} bot is now {result}");
await NotifyBotSubscriberAsync();
return Ok(result);
}
[HttpGet]
public async Task<List<TradingBot>> GetActiveBots()
{
return await GetBotList();
}
private async Task<List<TradingBot>> GetBotList()
{
var result = await _mediator.Send(new GetActiveBotsCommand());
var list = new List<TradingBot>();
foreach (var item in result)
{
list.Add(new TradingBot
{
Status = item.GetStatus(),
Name = item.GetName(),
Candles = item.Candles.ToList(),
Positions = item.Positions,
Signals = item.Signals.ToList(),
WinRate = item.GetWinRate(),
ProfitAndLoss = item.GetProfitAndLoss(),
Timeframe = item.Timeframe,
Ticker = item.Ticker,
AccountName = item.AccountName,
Scenario = item.Scenario,
IsForWatchingOnly = item.IsForWatchingOnly,
BotType = item.BotType,
MoneyManagement = item.MoneyManagement
});
}
return list;
}
private async Task NotifyBotSubscriberAsync()
{
var botsList = await GetBotList();
await _hubContext.Clients.All.SendAsync("BotsSubscription", botsList);
}
}

View File

@@ -0,0 +1,73 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.Workers.Abstractions;
using Managing.Domain.Candles;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
public class DataController : ControllerBase
{
private readonly IExchangeService _exchangeService;
private readonly IAccountService _accountService;
private readonly ICacheService _cacheService;
private readonly IStatisticService _statisticService;
private readonly IHubContext<CandleHub> _hubContext;
public DataController(
IExchangeService exchangeService,
IAccountService accountService,
ICacheService cacheService,
IStatisticService statisticService,
IHubContext<CandleHub> hubContext)
{
_exchangeService = exchangeService;
_accountService = accountService;
_cacheService = cacheService;
_statisticService = statisticService;
_hubContext = hubContext;
}
[HttpPost("GetTickers")]
public async Task<ActionResult<Ticker[]>> GetTickers(string accountName, Timeframe timeframe)
{
var account = await _accountService.GetAccount(accountName, true, false);
var cacheKey = string.Concat(accountName, timeframe.ToString());
var tickers = _cacheService.GetOrSave(cacheKey, () =>
{
return _exchangeService.GetTickers(account, timeframe).Result;
}, TimeSpan.FromHours(2));
return Ok(tickers);
}
[HttpGet("Spotlight")]
public ActionResult<SpotlightOverview> GetSpotlight()
{
var overview = _cacheService.GetOrSave(nameof(SpotlightOverview), () =>
{
return _statisticService.GetLastSpotlight(DateTime.Now.AddHours(-3));
}, TimeSpan.FromMinutes(2));
if (overview?.Spotlights.Count < overview?.ScenarioCount)
{
overview = _statisticService.GetLastSpotlight(DateTime.Now.AddHours(-3));
}
return Ok(overview);
}
[HttpGet("GetCandles")]
public async Task<ActionResult<List<Candle>>> GetCandles(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe)
{
return Ok(await _exchangeService.GetCandlesInflux(exchange, ticker, startDate, timeframe));
}
}

View File

@@ -0,0 +1,44 @@
using Managing.Application.Abstractions;
using Managing.Domain.MoneyManagements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class MoneyManagementController : ControllerBase
{
private readonly IMoneyManagementService _moneyManagementService;
public MoneyManagementController(IMoneyManagementService moneyManagementService)
{
_moneyManagementService = moneyManagementService;
}
[HttpPost]
public async Task<ActionResult<MoneyManagement>> PostMoneyManagement(MoneyManagement moneyManagement)
{
return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement));
}
[HttpGet]
[Route("moneymanagements")]
public ActionResult<IEnumerable<MoneyManagement>> GetMoneyManagements()
{
return Ok(_moneyManagementService.GetMoneyMangements());
}
[HttpGet]
public ActionResult<MoneyManagement> GetMoneyManagement(string name)
{
return Ok(_moneyManagementService.GetMoneyMangement(name));
}
[HttpDelete]
public ActionResult DeleteMoneyManagement(string name)
{
return Ok(_moneyManagementService.DeleteMoneyManagement(name));
}
}

View File

@@ -0,0 +1,83 @@
using Managing.Application.Abstractions;
using Managing.Domain.Scenarios;
using Managing.Domain.Strategies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class ScenarioController : ControllerBase
{
private readonly IScenarioService _scenarioService;
public ScenarioController(IScenarioService scenarioService)
{
_scenarioService = scenarioService;
}
[HttpGet]
public ActionResult<IEnumerable<Scenario>> GetScenarios()
{
return Ok(_scenarioService.GetScenarios());
}
[HttpPost]
public ActionResult<Scenario> CreateScenario(string name, List<string> strategies)
{
return Ok(_scenarioService.CreateScenario(name, strategies));
}
[HttpDelete]
public ActionResult DeleteScenario(string name)
{
return Ok(_scenarioService.DeleteScenario(name));
}
[HttpGet]
[Route("strategy")]
public ActionResult<IEnumerable<Strategy>> GetStrategies()
{
return Ok(_scenarioService.GetStrategies());
}
[HttpPost]
[Route("strategy")]
public ActionResult<Strategy> CreateStrategy(
StrategyType strategyType,
Timeframe timeframe,
string name,
int? period = null,
int? fastPeriods = null,
int? slowPeriods = null,
int? signalPeriods = null,
double? multiplier = null,
int? stochPeriods = null,
int? smoothPeriods = null,
int? cyclePeriods = null)
{
return Ok(_scenarioService.CreateStrategy(
strategyType,
timeframe,
name,
period,
fastPeriods,
slowPeriods,
signalPeriods,
multiplier,
stochPeriods,
smoothPeriods,
cyclePeriods));
}
[HttpDelete]
[Route("strategy")]
public ActionResult DeleteStrategy(string name)
{
return Ok(_scenarioService.DeleteStrategy(name));
}
}

View File

@@ -0,0 +1,30 @@
using Managing.Application.Abstractions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class SettingsController : ControllerBase
{
private readonly ISettingsService _settingsService;
public SettingsController(ISettingsService settingsService)
{
_settingsService = settingsService;
}
[HttpPost]
public ActionResult SetupSettings()
{
return Ok(_settingsService.SetupSettings());
}
[HttpDelete]
public async Task<ActionResult<bool>> ResetSettings()
{
return Ok(await _settingsService.ResetSettings());
}
}

View File

@@ -0,0 +1,107 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Trades;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
[ApiController]
[Authorize]
[Route("[controller]")]
public class TradingController : ControllerBase
{
private readonly ICommandHandler<OpenPositionRequest, Position> _openTradeCommandHandler;
private readonly ICommandHandler<ClosePositionCommand, Position> _closeTradeCommandHandler;
private readonly ITradingService _tradingService;
private readonly IMoneyManagementService _moneyManagementService;
private readonly IMediator _mediator;
private readonly ILogger<TradingController> _logger;
public TradingController(
ILogger<TradingController> logger,
ICommandHandler<OpenPositionRequest, Position> openTradeCommandHandler,
ICommandHandler<ClosePositionCommand, Position> closeTradeCommandHandler,
ITradingService tradingService,
IMediator mediator)
{
_logger = logger;
_openTradeCommandHandler = openTradeCommandHandler;
_closeTradeCommandHandler = closeTradeCommandHandler;
_tradingService = tradingService;
_mediator = mediator;
}
[HttpGet("GetPositions")]
public async Task<ActionResult<List<Position>>> GetPositions(PositionInitiator positionInitiator)
{
var result = await _mediator.Send(new GetPositionsCommand(positionInitiator));
return Ok(result);
}
[HttpGet("GetTrade")]
public async Task<ActionResult<Trade>> GetTrade(string accountName, Ticker ticker, string exchangeOrderId)
{
var result = await _mediator.Send(new GetTradeCommand(accountName, exchangeOrderId, ticker));
return Ok(result);
}
[HttpGet("GetTrades")]
public async Task<ActionResult<Trade>> GetTrades(string accountName, Ticker ticker, string exchangeOrderId)
{
var result = await _mediator.Send(new GetTradesCommand(ticker, accountName));
return Ok(result);
}
[HttpGet("ClosePosition")]
public async Task<ActionResult<Position>> ClosePosition(string identifier)
{
var position = _tradingService.GetPositionByIdentifier(identifier);
var result = await _closeTradeCommandHandler.Handle(new ClosePositionCommand(position));
return Ok(result);
}
[HttpGet("OpenPosition")]
public async Task<ActionResult<Position>> Trade(
string accountName,
string moneyManagementName,
TradeDirection direction,
Ticker ticker,
RiskLevel riskLevel,
bool isForPaperTrading,
MoneyManagement? moneyManagement = null,
decimal? openPrice = null)
{
if (string.IsNullOrEmpty(accountName))
{
throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
}
if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
{
throw new ArgumentException($"'{nameof(moneyManagementName)}' cannot be null or empty.", nameof(moneyManagementName));
}
if (moneyManagement == null)
{
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
}
var command = new OpenPositionRequest(
accountName,
moneyManagement,
direction,
ticker,
PositionInitiator.User,
DateTime.UtcNow,
isForPaperTrading: isForPaperTrading,
price: openPrice);
var result = await _openTradeCommandHandler.Handle(command);
return Ok(result);
}
}

View File

@@ -0,0 +1,39 @@
using Managing.Api.Authorization;
using Managing.Api.Models.Requests;
using Managing.Application.Abstractions.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers;
[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public class UserController : ControllerBase
{
private IConfiguration _config;
private readonly IUserService _userService;
private readonly IJwtUtils _jwtUtils;
public UserController(IConfiguration config, IUserService userService, IJwtUtils jwtUtils)
{
_config = config;
_userService = userService;
_jwtUtils = jwtUtils;
}
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult<string>> CreateToken([FromBody] LoginRequest login)
{
var user = await _userService.Authenticate(login.Name, login.Address, login.Message, login.Signature);
if (user != null)
{
var tokenString = _jwtUtils.GenerateJwtToken(user, login.Address);
return Ok(tokenString);
}
return Unauthorized();
}
}

View File

@@ -0,0 +1,45 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Workflows;
using Managing.Domain.Workflows.Synthetics;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Managing.Api.Controllers
{
[Authorize]
public class WorkflowController : BaseController
{
private readonly IWorkflowService _workflowService;
public WorkflowController(IWorkflowService WorkflowService, IUserService userService) : base(userService)
{
_workflowService = WorkflowService;
}
[HttpPost]
public async Task<ActionResult<Workflow>> PostWorkflow([ModelBinder]SyntheticWorkflow workflowRequest)
{
return Ok(await _workflowService.InsertOrUpdateWorkflow(workflowRequest));
}
[HttpGet]
public ActionResult<IEnumerable<SyntheticWorkflow>> GetWorkflows()
{
return Ok(_workflowService.GetWorkflows());
}
[HttpGet]
[Route("flows")]
public async Task<ActionResult<IEnumerable<IFlow>>> GetAvailableFlows()
{
return Ok(await _workflowService.GetAvailableFlows());
}
[HttpDelete]
public ActionResult DeleteWorkflow(string name)
{
return Ok(_workflowService.DeleteWorkflow(name));
}
}
}

View File

@@ -0,0 +1,36 @@
# Use the official Microsoft ASP.NET Core runtime as the base image.
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Use the official Microsoft .NET SDK image to build the code.
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["Managing.Api/Managing.Api.csproj", "Managing.Api/"]
COPY ["Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
COPY ["Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
COPY ["Managing.Application/Managing.Application.csproj", "Managing.Application/"]
COPY ["Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
COPY ["Managing.Common/Managing.Common.csproj", "Managing.Common/"]
COPY ["Managing.Core/Managing.Core.csproj", "Managing.Core/"]
COPY ["Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
COPY ["Managing.Domain/Managing.Domain.csproj", "Managing.Domain/"]
COPY ["Managing.Application.Workers/Managing.Application.Workers.csproj", "Managing.Application.Workers/"]
COPY ["Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj", "Managing.Infrastructure.Messengers/"]
COPY ["Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj", "Managing.Infrastructure.Exchanges/"]
COPY ["Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj", "Managing.Infrastructure.Database/"]
RUN dotnet restore "Managing.Api/Managing.Api.csproj"
COPY . .
WORKDIR "/src/Managing.Api"
RUN dotnet build "Managing.Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Managing.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
COPY Managing.Api/managing_cert.pfx .
COPY Managing.Api/appsettings.Lowpro.json .
ENTRYPOINT ["dotnet", "Managing.Api.dll"]

View File

@@ -0,0 +1,62 @@
using System.Net;
using System.Text.Json;
namespace Managing.Api.Exceptions;
public class GlobalErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public GlobalErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
HttpStatusCode status;
var exceptionType = exception.GetType();
if (exceptionType == typeof(Exception))
{
status = HttpStatusCode.InternalServerError;
}
else if (exceptionType == typeof(NotImplementedException))
{
status = HttpStatusCode.NotImplemented;
}
else if (exceptionType == typeof(UnauthorizedAccessException))
{
status = HttpStatusCode.Unauthorized;
}
else if (exceptionType == typeof(ArgumentException))
{
status = HttpStatusCode.Unauthorized;
}
else if (exceptionType == typeof(KeyNotFoundException))
{
status = HttpStatusCode.Unauthorized;
}
else
{
status = HttpStatusCode.InternalServerError;
}
var message = exception.Message;
var stackTrace = exception.StackTrace;
var exceptionResult = JsonSerializer.Serialize(new { error = message, stackTrace });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)status;
return context.Response.WriteAsync(exceptionResult);
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Managing.Api.Filters
{
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
model.Enum.Clear();
Enum.GetNames(context.Type)
.ToList()
.ForEach(n => model.Enum.Add(new OpenApiString(n)));
}
}
}
}

View File

@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>AnyCPU;x64</Platforms>
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
<UserSecretsId>7476db9f-ade4-414a-a420-e3ab55cb5f8d</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Essential.LoggerProvider.Elasticsearch" Version="1.3.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
<PackageReference Include="xunit" Version="2.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Managing.Bootstrap\Managing.Bootstrap.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Oda-sandbox.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Managing.Api.Models.Requests
{
public class CreateScenarioRequest
{
[Required]
public string Name { get; internal set; }
[Required]
public List<string> Strategies { get; internal set; }
}
}

View File

@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Requests;
public class CreateStrategyRequest
{
[Required]
public StrategyType Type { get; internal set; }
[Required]
public Timeframe Timeframe { get; internal set; }
[Required]
public string Name { get; internal set; }
[Required]
public int Period { get; internal set; }
}

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace Managing.Api.Models.Requests;
public class LoginRequest
{
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string Signature { get; set; }
[Required]
public string Message { get; set; }
}

View File

@@ -0,0 +1,15 @@
using static Managing.Common.Enums;
namespace Managing.Api.Models.Requests
{
public class RunBacktestRequest
{
public TradingExchanges Exchange { get; set; }
public BotType BotType { get; set; }
public Ticker Ticker { get; set; }
public Timeframe Timeframe { get; set; }
public RiskLevel RiskLevel { get; set; }
public bool WatchOnly { get; set; }
public int Days { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Requests
{
public class StartBotRequest
{
[Required]
public BotType BotType { get; set; }
[Required]
public string BotName { get; set; }
[Required]
public Ticker Ticker { get; set; }
[Required]
public Timeframe Timeframe { get; set; }
[Required]
public bool IsForWatchOnly { get; set; }
[Required]
public string Scenario { get; set; }
[Required]
public string AccountName { get; set; }
[Required]
public string MoneyManagementName { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using System.ComponentModel.DataAnnotations;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Responses
{
public class TradingBot
{
[Required]
public string Name { get; internal set; }
[Required]
public string Status { get; internal set; }
[Required]
public List<Signal> Signals { get; internal set; }
[Required]
public List<Position> Positions { get; internal set; }
[Required]
public List<Candle> Candles { get; internal set; }
[Required]
public RiskLevel RiskLevel { get; internal set; }
[Required]
public int WinRate { get; internal set; }
[Required]
public decimal ProfitAndLoss { get; internal set; }
[Required]
public Timeframe Timeframe { get; internal set; }
[Required]
public Ticker Ticker { get; internal set; }
[Required]
public string Scenario { get; internal set; }
[Required]
public TradingExchanges Exchange { get; internal set; }
[Required]
public bool IsForWatchingOnly { get; internal set; }
[Required]
public BotType BotType { get; internal set; }
[Required]
public string AccountName { get; internal set; }
[Required]
public MoneyManagement MoneyManagement { get; internal set; }
}
}

168
src/Managing.Api/Program.cs Normal file
View File

@@ -0,0 +1,168 @@
using System.Text;
using System.Text.Json.Serialization;
using Managing.Api.Authorization;
using Managing.Api.Exceptions;
using Managing.Api.Filters;
using Managing.Application.Hubs;
using Managing.Bootstrap;
using Managing.Common;
using Managing.Infrastructure.Databases.InfluxDb.Models;
using Managing.Infrastructure.Databases.MongoDb;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using NSwag;
using NSwag.Generation.Processors.Security;
using Serilog;
using Serilog.Sinks.Elasticsearch;
// Builder
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.Lowpro.json", optional: true, reloadOnChange: true)
.AddJsonFile($"config.{builder.Environment.EnvironmentName}.json",
optional: true, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
builder.Configuration.AddUserSecrets<Program>();
builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
{
var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-");
var indexFormat = $"managing-{envName}-" + "{0:yyyy.MM.dd}";
var yourTemplateName = "dotnetlogs";
var es = new ElasticsearchSinkOptions(new Uri(hostBuilder.Configuration["ElasticConfiguration:Uri"]))
{
IndexFormat = indexFormat.ToLower(),
AutoRegisterTemplate = true,
OverwriteTemplate = true,
TemplateName = yourTemplateName,
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
TypeName = null,
BatchAction = ElasticOpType.Create,
MinimumLogEventLevel = Serilog.Events.LogEventLevel.Information,
FailureCallback = e => Console.WriteLine($"Unable to submit event {e.RenderMessage()} to ElasticSearch. " +
$"Full message : " + e.Exception.Message),
DetectElasticsearchVersion = true,
RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
};
loggerConfiguration
.WriteTo.Console()
.WriteTo.Elasticsearch(es);
});
builder.Services.AddOptions();
builder.Services.Configure<ManagingDatabaseSettings>(builder.Configuration.GetSection(Constants.Databases.MongoDb));
builder.Services.Configure<InfluxDbSettings>(builder.Configuration.GetSection(Constants.Databases.InfluxDb));
builder.Services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = builder.Configuration["Authentication:Schemes:Bearer:ValidIssuer"],
ValidAudience = builder.Configuration["Authentication:Schemes:Bearer:ValidAudiences"],
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])),
ValidateIssuer = false,
ValidateAudience = false,
ValidateIssuerSigningKey = true
};
});
builder.Services.AddAuthorization();
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.SetIsOriginAllowed((host) => true)
.AllowAnyOrigin()
.WithOrigins("http://localhost:3000/")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
builder.Services.AddSignalR().AddJsonProtocol();
builder.Services.AddScoped<IJwtUtils, JwtUtils>();
builder.Services.RegisterApiDependencies(builder.Configuration);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(document =>
{
document.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
{
Type = OpenApiSecuritySchemeType.ApiKey,
Name = "Authorization",
In = OpenApiSecurityApiKeyLocation.Header,
Description = "Type into the textbox: Bearer {your JWT token}."
});
document.OperationProcessors.Add(
new AspNetCoreOperationSecurityScopeProcessor("JWT"));
});
builder.Services.AddSwaggerGen(options =>
{
options.SchemaFilter<EnumSchemaFilter>();
options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
{
Description = "Please insert your JWT Token into field : Bearer {your_token}",
Name = "Authorization",
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
Scheme = "Bearer",
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement{
{
new Microsoft.OpenApi.Models.OpenApiSecurityScheme{
Reference = new Microsoft.OpenApi.Models.OpenApiReference{
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[]{}
}
});
});
builder.WebHost.SetupDiscordBot();
// App
var app = builder.Build();
app.UseSerilogRequestLogging();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseOpenApi();
app.UseSwaggerUi3();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing API v1");
c.RoutePrefix = string.Empty;
});
app.UseCors("CorsPolicy");
app.UseMiddleware(typeof(GlobalErrorHandlingMiddleware));
app.UseMiddleware<JwtMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<BotHub>("/bothub");
endpoints.MapHub<BacktestHub>("/backtesthub");
endpoints.MapHub<CandleHub>("/candlehub");
});
app.Run();

View File

@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Error",
"System": "Error",
"Microsoft": "Warning"
}
},
"AllowedHosts": "*",
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200/"
}
}

View File

@@ -0,0 +1,34 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://localhost:8086/",
"Organization": "",
"Token": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
"Discord": {
"ApplicationId": "",
"PublicKey": "",
"SignalChannelId": 1018897743118340180,
"TroublesChannelId": 1018897743118340180,
"TradesChannelId": 1020457417877753886,
"RequestChannelId": 1020463151034138694,
"RequestsChannelId": 1020463151034138694,
"ButtonExpirationMinutes": 2
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,46 @@
{
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [ "http://localhost:3000/" ],
"ValidIssuer": "Managing"
}
}
},
"Jwt": {
"Secret": "2ed5f490-b6c1-4cad-8824-840c911f1fe6"
},
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "managing-org",
"Token": "OPjdwQBmKr0zQecJ10IDQ4bt32oOJzmFp687QWWzbGeyH0R-gCA6HnXI_B0oQ_InPmSUXKFje8DSAUPbY0hn-w=="
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
"Discord": {
"ApplicationId": "966075382002516031",
"PublicKey": "63028f6bb740cd5d26ae0340b582dee2075624011b28757436255fc002ca8a7c",
"TokenId": "OTY2MDc1MzgyMDAyNTE2MDMx.Yl8dzw.xpeIAaMwGrwTNY4r9JYv0ebzb-U",
"SignalChannelId": 1134858150667898910,
"TradesChannelId": 1134858092530634864,
"TroublesChannelId": 1134858233031446671,
"CopyTradingChannelId": 1134857874896588881,
"RequestsChannelId": 1018589494968078356,
"ButtonExpirationMinutes": 10
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,37 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb:27017",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "",
"Token": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
"Discord": {
"BotActivity": "trading strategies",
"HandleUserAction": true,
"ApplicationId": "",
"PublicKey": "",
"TokenId": "",
"SignalChannelId": 966080506473099314,
"TradesChannelId": 998374177763491851,
"TroublesChannelId": 1015761955321040917,
"CopyTradingChannelId": 1132022887012909126,
"RequestsChannelId": 1018589494968078356,
"ButtonExpirationMinutes": 10
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,36 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb:27017",
"DatabaseName": "ManagingDb",
"UserName": "admin",
"Password": "!MotdepasseFort11"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "managing-org",
"Token": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
"Discord": {
"ApplicationId": "",
"PublicKey": "",
"TokenId": "",
"SignalChannelId": 966080506473099314,
"TradesChannelId": 998374177763491851,
"TroublesChannelId": 1015761955321040917,
"RequestsChannelId": 1018589494968078356,
"ButtonExpirationMinutes": 2
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,34 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://localhost:8086/",
"Organization": "",
"Token": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
"Discord": {
"ApplicationId": "",
"PublicKey": "",
"TokenId": "",
"SignalChannelId": 966080506473099314,
"TradesChannelId": 998374177763491851,
"TroublesChannelId": 1015761955321040917,
"RequestsChannelId": 1018589494968078356,
"ButtonExpirationMinutes": 2
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,48 @@
{
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [ "http://localhost:3000/" ],
"ValidIssuer": "Managing"
}
}
},
"Jwt": {
"Secret": "2ed5f490-b6c1-4cad-8824-840c911f1fe6"
},
"ManagingDatabase": {
"ConnectionString": "mongodb://managingdb",
"DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "",
"Token": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200/"
},
"Discord": {
"BotActivity": "trading strategies",
"HandleUserAction": true,
"ApplicationId": "",
"PublicKey": "",
"TokenId": "",
"SignalChannelId": 966080506473099314,
"TradesChannelId": 998374177763491851,
"TroublesChannelId": 1015761955321040917,
"CopyTradingChannelId": 1132022887012909126,
"RequestsChannelId": 1018589494968078356,
"ButtonExpirationMinutes": 10
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,4 @@
{
"schemaVersion": 2,
"dockerfilePath": "Dockerfile"
}

View File

@@ -0,0 +1,19 @@
{"Timestamp":"2022-09-13T02:59:02.0040536+02:00","Level":"Information","MessageTemplate":"02:59:01 Discord Discord.Net v3.8.0 (API v9)","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.0892523+02:00","Level":"Information","MessageTemplate":"02:59:02 Gateway Connecting","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.2504922+02:00","Level":"Information","MessageTemplate":"Now listening on: {address}","Properties":{"address":"https://localhost:5001","EventId":{"Id":14,"Name":"ListeningOnAddress"},"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.2556804+02:00","Level":"Information","MessageTemplate":"Now listening on: {address}","Properties":{"address":"http://localhost:5000","EventId":{"Id":14,"Name":"ListeningOnAddress"},"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.2591276+02:00","Level":"Information","MessageTemplate":"Application started. Press Ctrl+C to shut down.","Properties":{"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.2625140+02:00","Level":"Information","MessageTemplate":"Hosting environment: {envName}","Properties":{"envName":"Development","SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:02.2651704+02:00","Level":"Information","MessageTemplate":"Content root path: {contentRoot}","Properties":{"contentRoot":"C:\\Users\\Utilisateur\\Desktop\\Projects\\apps\\Managing\\src\\Managing.Api\\","SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:03.3572754+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway You're using the GuildScheduledEvents gateway intent without listening to any events related to that intent, consider removing the intent from your config.","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:03.3628433+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway You're using the GuildInvites gateway intent without listening to any events related to that intent, consider removing the intent from your config.","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:03.3688717+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway Connected","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:03.9425506+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway Ready","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
{"Timestamp":"2022-09-13T02:59:10.5882097+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/index.html","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/index.html - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000001","RequestPath":"/index.html","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/index.html - -"}]}}
{"Timestamp":"2022-09-13T02:59:11.5711332+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":996.3451,"StatusCode":200,"ContentType":"text/html;charset=utf-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/index.html","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/index.html - - - 200 - text/html;charset=utf-8 996.3451ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000001","RequestPath":"/index.html","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/index.html - - - 200 - text/html;charset=utf-8 996.3451ms"}]}}
{"Timestamp":"2022-09-13T02:59:11.6123850+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_framework/aspnetcore-browser-refresh.js","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000003","RequestPath":"/_framework/aspnetcore-browser-refresh.js","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - -"}]}}
{"Timestamp":"2022-09-13T02:59:11.6281716+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":15.5581,"StatusCode":200,"ContentType":"application/javascript; charset=utf-8","ContentLength":11994,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_framework/aspnetcore-browser-refresh.js","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - - - 200 11994 application/javascript;+charset=utf-8 15.5581ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000003","RequestPath":"/_framework/aspnetcore-browser-refresh.js","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - - - 200 11994 application/javascript;+charset=utf-8 15.5581ms"}]}}
{"Timestamp":"2022-09-13T02:59:11.7015500+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_vs/browserLink","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/_vs/browserLink - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000005","RequestPath":"/_vs/browserLink","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/_vs/browserLink - -"}]}}
{"Timestamp":"2022-09-13T02:59:11.8214511+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":119.7799,"StatusCode":200,"ContentType":"text/javascript; charset=UTF-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_vs/browserLink","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/_vs/browserLink - - - 200 - text/javascript;+charset=UTF-8 119.7799ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000005","RequestPath":"/_vs/browserLink","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/_vs/browserLink - - - 200 - text/javascript;+charset=UTF-8 119.7799ms"}]}}
{"Timestamp":"2022-09-13T02:59:11.9652804+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/swagger/v1/swagger.json","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000007","RequestPath":"/swagger/v1/swagger.json","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - -"}]}}
{"Timestamp":"2022-09-13T02:59:12.3915820+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":426.2987,"StatusCode":200,"ContentType":"application/json;charset=utf-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/swagger/v1/swagger.json","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - - - 200 - application/json;charset=utf-8 426.2987ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000007","RequestPath":"/swagger/v1/swagger.json","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - - - 200 - application/json;charset=utf-8 426.2987ms"}]}}