руфдер
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
namespace PepperBot.Application.Interfaces;
|
||||||
|
|
||||||
|
public interface IHealthMonitor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Отметить успешный запрос к Pepper, вернувший более одной скидки.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timestamp">Время успешного запроса (UTC).</param>
|
||||||
|
void ReportSuccess(DateTimeOffset timestamp);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Время последнего успешного запроса или null, если их ещё не было.
|
||||||
|
/// </summary>
|
||||||
|
DateTimeOffset? LastSuccessfulRequestAt { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Текущее состояние healthcheck.
|
||||||
|
/// </summary>
|
||||||
|
bool IsHealthy(TimeSpan successTtl);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -10,23 +10,32 @@ public class DealService
|
|||||||
private readonly IDealRepository _repository;
|
private readonly IDealRepository _repository;
|
||||||
private readonly ITelegramNotifier _notifier;
|
private readonly ITelegramNotifier _notifier;
|
||||||
private readonly ILogger<DealService> _logger;
|
private readonly ILogger<DealService> _logger;
|
||||||
|
private readonly IHealthMonitor _healthMonitor;
|
||||||
|
|
||||||
public DealService(
|
public DealService(
|
||||||
IPepperClient pepperClient,
|
IPepperClient pepperClient,
|
||||||
IDealRepository repository,
|
IDealRepository repository,
|
||||||
ITelegramNotifier notifier,
|
ITelegramNotifier notifier,
|
||||||
ILogger<DealService> logger)
|
ILogger<DealService> logger,
|
||||||
|
IHealthMonitor healthMonitor)
|
||||||
{
|
{
|
||||||
_pepperClient = pepperClient;
|
_pepperClient = pepperClient;
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
_notifier = notifier;
|
_notifier = notifier;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_healthMonitor = healthMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CheckAndNotifyAsync(CancellationToken cancellationToken)
|
public async Task CheckAndNotifyAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var deals = await _pepperClient.GetLatestDealsAsync(cancellationToken);
|
var deals = await _pepperClient.GetLatestDealsAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Хелсчек: успешный запрос, вернувший более одной скидки (пусть и не новой)
|
||||||
|
if (deals.Count > 1)
|
||||||
|
{
|
||||||
|
_healthMonitor.ReportSuccess(DateTimeOffset.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var deal in deals)
|
foreach (var deal in deals)
|
||||||
{
|
{
|
||||||
if (await _repository.ExistsAsync(deal.Id, cancellationToken))
|
if (await _repository.ExistsAsync(deal.Id, cancellationToken))
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using PepperBot.Application.Interfaces;
|
||||||
|
|
||||||
|
namespace PepperBot.Infrastructure.Health;
|
||||||
|
|
||||||
|
public class PepperHealthMonitor : IHealthMonitor
|
||||||
|
{
|
||||||
|
private DateTimeOffset? _lastSuccessfulRequestAt;
|
||||||
|
|
||||||
|
public DateTimeOffset? LastSuccessfulRequestAt => _lastSuccessfulRequestAt;
|
||||||
|
|
||||||
|
public void ReportSuccess(DateTimeOffset timestamp)
|
||||||
|
{
|
||||||
|
// Просто запоминаем последнее успешное время; избыточную синхронизацию не вводим
|
||||||
|
// так как запись идёт по одному воркеру.
|
||||||
|
_lastSuccessfulRequestAt = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsHealthy(TimeSpan successTtl)
|
||||||
|
{
|
||||||
|
var last = _lastSuccessfulRequestAt;
|
||||||
|
if (last is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTimeOffset.UtcNow - last.Value < successTtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
||||||
|
|||||||
+26
-6
@@ -1,14 +1,16 @@
|
|||||||
using PepperBot;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PepperBot.Application.Services;
|
using PepperBot;
|
||||||
using PepperBot.Application.Interfaces;
|
using PepperBot.Application.Interfaces;
|
||||||
|
using PepperBot.Application.Services;
|
||||||
using PepperBot.Infrastructure.Data;
|
using PepperBot.Infrastructure.Data;
|
||||||
|
using PepperBot.Infrastructure.Health;
|
||||||
using PepperBot.Infrastructure.Pepper;
|
using PepperBot.Infrastructure.Pepper;
|
||||||
using PepperBot.Infrastructure.Telegram;
|
using PepperBot.Infrastructure.Telegram;
|
||||||
|
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddLogging(logging =>
|
builder.Services.AddLogging(logging =>
|
||||||
{
|
{
|
||||||
@@ -19,10 +21,28 @@ builder.Services.AddLogging(logging =>
|
|||||||
builder.Services.AddSingleton<IDealRepository, SqliteDealRepository>();
|
builder.Services.AddSingleton<IDealRepository, SqliteDealRepository>();
|
||||||
builder.Services.AddSingleton<IPepperClient, PepperClient>();
|
builder.Services.AddSingleton<IPepperClient, PepperClient>();
|
||||||
builder.Services.AddSingleton<ITelegramNotifier, TelegramNotifier>();
|
builder.Services.AddSingleton<ITelegramNotifier, TelegramNotifier>();
|
||||||
|
builder.Services.AddSingleton<IHealthMonitor, PepperHealthMonitor>();
|
||||||
builder.Services.AddSingleton<DealService>();
|
builder.Services.AddSingleton<DealService>();
|
||||||
|
|
||||||
builder.Services.AddHostedService<BotWorker>();
|
builder.Services.AddHostedService<BotWorker>();
|
||||||
|
|
||||||
var host = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
await host.RunAsync();
|
app.MapGet("/health", (IHealthMonitor healthMonitor) =>
|
||||||
|
{
|
||||||
|
var successTtl = TimeSpan.FromMinutes(15);
|
||||||
|
var isHealthy = healthMonitor.IsHealthy(successTtl);
|
||||||
|
|
||||||
|
if (isHealthy)
|
||||||
|
{
|
||||||
|
return Results.Ok(new
|
||||||
|
{
|
||||||
|
status = "Healthy",
|
||||||
|
lastSuccessfulRequestAt = healthMonitor.LastSuccessfulRequestAt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.RunAsync();
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||||
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
||||||
|
- ASPNETCORE_URLS=http://0.0.0.0:8080
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
ports:
|
||||||
|
- "9001:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user