142 lines
4.8 KiB
C#
142 lines
4.8 KiB
C#
using System.Text.Json;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using SPMS.Application.DTOs.Webhook;
|
|
using SPMS.Application.Interfaces;
|
|
using SPMS.Domain.Entities;
|
|
using SPMS.Domain.Enums;
|
|
using SPMS.Domain.Interfaces;
|
|
|
|
namespace SPMS.Infrastructure.Webhook;
|
|
|
|
public class WebhookService : IWebhookService
|
|
{
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly ILogger<WebhookService> _logger;
|
|
|
|
private const int MaxRetries = 3;
|
|
private const int RetryDelaySeconds = 30;
|
|
private const int TimeoutSeconds = 10;
|
|
|
|
public WebhookService(
|
|
IServiceProvider serviceProvider,
|
|
IHttpClientFactory httpClientFactory,
|
|
ILogger<WebhookService> logger)
|
|
{
|
|
_serviceProvider = serviceProvider;
|
|
_httpClientFactory = httpClientFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
public Task SendAsync(long serviceId, WebhookEvent eventType, object data)
|
|
{
|
|
_ = Task.Run(() => SendWithRetryAsync(serviceId, eventType, data));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task SendWithRetryAsync(long serviceId, WebhookEvent eventType, object data)
|
|
{
|
|
using var scope = _serviceProvider.CreateScope();
|
|
var serviceRepo = scope.ServiceProvider.GetRequiredService<IServiceRepository>();
|
|
var webhookLogRepo = scope.ServiceProvider.GetRequiredService<IWebhookLogRepository>();
|
|
var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
|
|
|
|
var service = await serviceRepo.GetByIdAsync(serviceId);
|
|
if (service == null || string.IsNullOrEmpty(service.WebhookUrl))
|
|
return;
|
|
|
|
var eventName = EventTypeToString(eventType);
|
|
if (!IsEventSubscribed(service, eventName))
|
|
return;
|
|
|
|
var payload = new WebhookPayloadDto
|
|
{
|
|
EventType = eventName,
|
|
Timestamp = DateTime.UtcNow,
|
|
ServiceCode = service.ServiceCode,
|
|
Data = data
|
|
};
|
|
|
|
var jsonPayload = JsonSerializer.Serialize(payload);
|
|
var client = _httpClientFactory.CreateClient("Webhook");
|
|
client.Timeout = TimeSpan.FromSeconds(TimeoutSeconds);
|
|
|
|
int? responseCode = null;
|
|
string? responseBody = null;
|
|
var success = false;
|
|
|
|
for (var attempt = 1; attempt <= MaxRetries; attempt++)
|
|
{
|
|
try
|
|
{
|
|
var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json");
|
|
var response = await client.PostAsync(service.WebhookUrl, content);
|
|
|
|
responseCode = (int)response.StatusCode;
|
|
responseBody = await response.Content.ReadAsStringAsync();
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
success = true;
|
|
_logger.LogInformation("Webhook sent successfully to {Url} (attempt {Attempt})", service.WebhookUrl, attempt);
|
|
break;
|
|
}
|
|
|
|
_logger.LogWarning("Webhook failed with status {StatusCode} (attempt {Attempt}/{MaxRetries})",
|
|
responseCode, attempt, MaxRetries);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
responseBody = ex.Message;
|
|
_logger.LogWarning(ex, "Webhook request failed (attempt {Attempt}/{MaxRetries})", attempt, MaxRetries);
|
|
}
|
|
|
|
if (attempt < MaxRetries)
|
|
await Task.Delay(TimeSpan.FromSeconds(RetryDelaySeconds));
|
|
}
|
|
|
|
var log = new WebhookLog
|
|
{
|
|
ServiceId = serviceId,
|
|
WebhookUrl = service.WebhookUrl,
|
|
EventType = eventType,
|
|
Payload = jsonPayload,
|
|
Status = success ? WebhookStatus.Success : WebhookStatus.Failed,
|
|
ResponseCode = responseCode,
|
|
ResponseBody = responseBody?.Length > 2000 ? responseBody[..2000] : responseBody,
|
|
SentAt = DateTime.UtcNow
|
|
};
|
|
|
|
await webhookLogRepo.AddAsync(log);
|
|
await unitOfWork.SaveChangesAsync();
|
|
}
|
|
|
|
private static bool IsEventSubscribed(Service service, string eventName)
|
|
{
|
|
if (string.IsNullOrEmpty(service.WebhookEvents))
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var events = JsonSerializer.Deserialize<List<string>>(service.WebhookEvents);
|
|
return events?.Contains(eventName) == true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static string EventTypeToString(WebhookEvent eventType)
|
|
{
|
|
return eventType switch
|
|
{
|
|
WebhookEvent.PushSent => "push_sent",
|
|
WebhookEvent.PushFailed => "push_failed",
|
|
WebhookEvent.PushClicked => "push_clicked",
|
|
_ => eventType.ToString().ToLowerInvariant()
|
|
};
|
|
}
|
|
}
|