223 lines
7.7 KiB
C#
223 lines
7.7 KiB
C#
using Managing.Application.Abstractions.Repositories;
|
|
using Managing.Application.Abstractions.Services;
|
|
using Managing.Domain.Whitelist;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Managing.Application.Whitelist;
|
|
|
|
public class WhitelistService : IWhitelistService
|
|
{
|
|
private readonly IWhitelistRepository _whitelistRepository;
|
|
private readonly IWebhookService _webhookService;
|
|
private readonly ILogger<WhitelistService> _logger;
|
|
private const string AlertsChannel = "2676086723";
|
|
|
|
public WhitelistService(
|
|
IWhitelistRepository whitelistRepository,
|
|
IWebhookService webhookService,
|
|
ILogger<WhitelistService> logger)
|
|
{
|
|
_whitelistRepository = whitelistRepository;
|
|
_webhookService = webhookService;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<(IEnumerable<WhitelistAccount> Accounts, int TotalCount)> GetPaginatedAsync(
|
|
int pageNumber,
|
|
int pageSize,
|
|
string? searchExternalEthereumAccount = null,
|
|
string? searchTwitterAccount = null)
|
|
{
|
|
if (pageNumber < 1)
|
|
{
|
|
throw new ArgumentException("Page number must be greater than 0", nameof(pageNumber));
|
|
}
|
|
|
|
if (pageSize < 1 || pageSize > 100)
|
|
{
|
|
throw new ArgumentException("Page size must be between 1 and 100", nameof(pageSize));
|
|
}
|
|
|
|
return await _whitelistRepository.GetPaginatedAsync(
|
|
pageNumber,
|
|
pageSize,
|
|
searchExternalEthereumAccount,
|
|
searchTwitterAccount);
|
|
}
|
|
|
|
public async Task<int> SetIsWhitelistedAsync(IEnumerable<int> accountIds, bool isWhitelisted)
|
|
{
|
|
var idsList = accountIds?.ToList() ?? new List<int>();
|
|
|
|
if (!idsList.Any())
|
|
{
|
|
throw new ArgumentException("At least one account ID must be provided", nameof(accountIds));
|
|
}
|
|
|
|
_logger.LogInformation("Setting IsWhitelisted to {IsWhitelisted} for {Count} account(s)",
|
|
isWhitelisted, idsList.Count);
|
|
|
|
var updatedCount = await _whitelistRepository.SetIsWhitelistedAsync(idsList, isWhitelisted);
|
|
|
|
_logger.LogInformation("Successfully updated {Count} account(s) to IsWhitelisted = {IsWhitelisted}",
|
|
updatedCount, isWhitelisted);
|
|
|
|
// Send notification to Alert channel when users are whitelisted
|
|
if (isWhitelisted && updatedCount > 0)
|
|
{
|
|
try
|
|
{
|
|
await SendWhitelistNotificationAsync(idsList);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to send whitelist notification for account IDs: {AccountIds}",
|
|
string.Join(", ", idsList));
|
|
SentrySdk.CaptureException(ex);
|
|
// Don't throw - notification failure shouldn't fail the whitelist operation
|
|
}
|
|
}
|
|
|
|
return updatedCount;
|
|
}
|
|
|
|
private async Task SendWhitelistNotificationAsync(List<int> accountIds)
|
|
{
|
|
var accounts = new List<WhitelistAccount>();
|
|
|
|
// Fetch account details for the notification
|
|
foreach (var id in accountIds)
|
|
{
|
|
var account = await _whitelistRepository.GetByIdAsync(id);
|
|
if (account != null)
|
|
{
|
|
accounts.Add(account);
|
|
}
|
|
}
|
|
|
|
if (!accounts.Any())
|
|
{
|
|
_logger.LogWarning("No accounts found to send whitelist notification for IDs: {AccountIds}",
|
|
string.Join(", ", accountIds));
|
|
return;
|
|
}
|
|
|
|
// Build notification message
|
|
var message = accounts.Count == 1
|
|
? BuildSingleAccountWhitelistMessage(accounts[0])
|
|
: BuildMultipleAccountsWhitelistMessage(accounts);
|
|
|
|
await _webhookService.SendMessage(message, AlertsChannel);
|
|
|
|
_logger.LogInformation("Sent whitelist notification to Alert channel for {Count} account(s)", accounts.Count);
|
|
}
|
|
|
|
private string BuildSingleAccountWhitelistMessage(WhitelistAccount account)
|
|
{
|
|
var message = $"✅ **User Whitelisted**\n" +
|
|
$"Account ID: `{account.Id}`\n" +
|
|
$"Privy ID: `{account.PrivyId}`\n" +
|
|
$"Embedded Wallet: `{account.EmbeddedWallet}`\n";
|
|
|
|
if (!string.IsNullOrWhiteSpace(account.ExternalEthereumAccount))
|
|
{
|
|
message += $"External Ethereum: `{account.ExternalEthereumAccount}`\n";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(account.TwitterAccount))
|
|
{
|
|
message += $"Twitter: `{account.TwitterAccount}`\n";
|
|
}
|
|
|
|
message += $"Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
|
|
|
return message;
|
|
}
|
|
|
|
private string BuildMultipleAccountsWhitelistMessage(List<WhitelistAccount> accounts)
|
|
{
|
|
var message = $"✅ **{accounts.Count} Users Whitelisted**\n\n";
|
|
|
|
foreach (var account in accounts)
|
|
{
|
|
message += $"• Account ID: `{account.Id}` | Privy ID: `{account.PrivyId}`\n";
|
|
message += $" Wallet: `{account.EmbeddedWallet}`\n";
|
|
|
|
if (!string.IsNullOrWhiteSpace(account.ExternalEthereumAccount))
|
|
{
|
|
message += $" External Ethereum: `{account.ExternalEthereumAccount}`\n";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(account.TwitterAccount))
|
|
{
|
|
message += $" Twitter: `{account.TwitterAccount}`\n";
|
|
}
|
|
|
|
message += "\n";
|
|
}
|
|
|
|
message += $"Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
|
|
|
return message;
|
|
}
|
|
|
|
public async Task<WhitelistAccount?> GetByIdAsync(int id)
|
|
{
|
|
return await _whitelistRepository.GetByIdAsync(id);
|
|
}
|
|
|
|
public async Task<bool> IsEmbeddedWalletWhitelistedAsync(string embeddedWallet)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(embeddedWallet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var account = await _whitelistRepository.GetByEmbeddedWalletAsync(embeddedWallet);
|
|
|
|
return account?.IsWhitelisted ?? false;
|
|
}
|
|
|
|
public async Task<WhitelistAccount> ProcessPrivyWebhookAsync(
|
|
string privyUserId,
|
|
long privyCreatedAt,
|
|
string walletAddress,
|
|
string? externalEthereumAccount,
|
|
string? twitterAccount)
|
|
{
|
|
_logger.LogInformation(
|
|
"Processing Privy webhook - PrivyId: {PrivyId}, Wallet: {Wallet}, ExternalEthereum: {ExternalEthereum}, Twitter: {Twitter}",
|
|
privyUserId, walletAddress, externalEthereumAccount ?? "null", twitterAccount ?? "null");
|
|
|
|
// Convert Unix timestamp to UTC DateTime (PostgreSQL requires UTC)
|
|
var privyCreationDate = DateTimeOffset.FromUnixTimeSeconds(privyCreatedAt).UtcDateTime;
|
|
|
|
// Check if account already exists
|
|
var existing = await _whitelistRepository.GetByPrivyIdAsync(privyUserId) ??
|
|
await _whitelistRepository.GetByEmbeddedWalletAsync(walletAddress);
|
|
|
|
var whitelistAccount = new WhitelistAccount
|
|
{
|
|
PrivyId = privyUserId,
|
|
PrivyCreationDate = privyCreationDate,
|
|
EmbeddedWallet = walletAddress,
|
|
ExternalEthereumAccount = externalEthereumAccount,
|
|
TwitterAccount = twitterAccount,
|
|
IsWhitelisted = false, // Default to false, admin will set to true later
|
|
CreatedAt = existing?.CreatedAt ?? DateTime.UtcNow
|
|
};
|
|
|
|
// If existing, preserve the ID
|
|
if (existing != null)
|
|
{
|
|
whitelistAccount.Id = existing.Id;
|
|
}
|
|
|
|
var result = await _whitelistRepository.CreateOrUpdateAsync(whitelistAccount);
|
|
|
|
_logger.LogInformation("Privy webhook processed successfully - AccountId: {Id}, PrivyId: {PrivyId}",
|
|
result.Id, result.PrivyId);
|
|
|
|
return result;
|
|
}
|
|
} |