Configure BTCPay with external nodes via Tor
- Set up Tor container for SOCKS proxy (port 9050) - Configured Monero wallet with remote onion node - Bitcoin node continues syncing in background (60% complete) - Created documentation for wallet configuration steps - All external connections routed through Tor for privacy BTCPay requires manual wallet configuration through web interface: - Bitcoin: Need to add xpub/zpub for watch-only wallet - Monero: Need to add address and view key System ready for payment acceptance once wallets configured.
This commit is contained in:
165
LittleShop/Areas/Admin/Controllers/BotRecoveryController.cs
Normal file
165
LittleShop/Areas/Admin/Controllers/BotRecoveryController.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using LittleShop.Services;
|
||||
|
||||
namespace LittleShop.Areas.Admin.Controllers;
|
||||
|
||||
[Area("Admin")]
|
||||
[Authorize(Policy = "AdminOnly")]
|
||||
public class BotRecoveryController : Controller
|
||||
{
|
||||
private readonly IBotContactService _contactService;
|
||||
private readonly IBotService _botService;
|
||||
private readonly ILogger<BotRecoveryController> _logger;
|
||||
|
||||
public BotRecoveryController(
|
||||
IBotContactService contactService,
|
||||
IBotService botService,
|
||||
ILogger<BotRecoveryController> logger)
|
||||
{
|
||||
_contactService = contactService;
|
||||
_botService = botService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// GET: Admin/BotRecovery
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var allBots = await _botService.GetAllBotsAsync();
|
||||
var botsWithStatus = allBots.Select(bot => new
|
||||
{
|
||||
Bot = bot,
|
||||
Contacts = _contactService.GetBotContactsAsync(bot.Id).Result,
|
||||
IsHealthy = bot.Status == Enums.BotStatus.Active &&
|
||||
bot.LastSeenAt > DateTime.UtcNow.AddMinutes(-5)
|
||||
}).ToList();
|
||||
|
||||
ViewData["BotsWithStatus"] = botsWithStatus;
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: Admin/BotRecovery/PrepareRecovery/{botId}
|
||||
public async Task<IActionResult> PrepareRecovery(Guid botId)
|
||||
{
|
||||
var recoveryData = await _contactService.PrepareContactRecoveryAsync(botId);
|
||||
return View(recoveryData);
|
||||
}
|
||||
|
||||
// POST: Admin/BotRecovery/ExecuteRecovery
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ExecuteRecovery(Guid fromBotId, Guid toBotId, bool notifyUsers = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Migrate contacts
|
||||
var success = await _contactService.MigrateContactsAsync(fromBotId, toBotId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
TempData["Error"] = "Failed to migrate contacts. Check logs for details.";
|
||||
return RedirectToAction(nameof(PrepareRecovery), new { botId = fromBotId });
|
||||
}
|
||||
|
||||
// Get the new bot info for notifications
|
||||
if (notifyUsers)
|
||||
{
|
||||
var toBot = await _botService.GetBotByIdAsync(toBotId);
|
||||
var orphanedContacts = await _contactService.GetOrphanedContactsAsync(fromBotId);
|
||||
|
||||
foreach (var contact in orphanedContacts)
|
||||
{
|
||||
await _contactService.NotifyContactOfBotChangeAsync(
|
||||
contact.TelegramUserId,
|
||||
toBot.PlatformUsername);
|
||||
}
|
||||
}
|
||||
|
||||
// Update bot statuses
|
||||
await _botService.UpdateBotStatusAsync(fromBotId, Enums.BotStatus.Retired);
|
||||
|
||||
TempData["Success"] = $"Successfully migrated contacts from bot {fromBotId} to {toBotId}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to execute bot recovery");
|
||||
TempData["Error"] = "An error occurred during recovery. Please try again.";
|
||||
return RedirectToAction(nameof(PrepareRecovery), new { botId = fromBotId });
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Admin/BotRecovery/Backup/{botId}
|
||||
public async Task<IActionResult> Backup(Guid botId)
|
||||
{
|
||||
var backup = await _contactService.CreateContactBackupAsync(botId);
|
||||
|
||||
// Return as downloadable JSON file
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(backup, new System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
|
||||
var bytes = System.Text.Encoding.UTF8.GetBytes(json);
|
||||
return File(bytes, "application/json", $"bot-contacts-{botId}-{DateTime.UtcNow:yyyyMMdd}.json");
|
||||
}
|
||||
|
||||
// POST: Admin/BotRecovery/RestoreBackup
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RestoreBackup(Guid toBotId, IFormFile backupFile)
|
||||
{
|
||||
if (backupFile == null || backupFile.Length == 0)
|
||||
{
|
||||
TempData["Error"] = "Please select a backup file to restore";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = backupFile.OpenReadStream();
|
||||
using var reader = new System.IO.StreamReader(stream);
|
||||
var json = await reader.ReadToEndAsync();
|
||||
|
||||
var backup = System.Text.Json.JsonSerializer.Deserialize<ContactBackupDto>(json);
|
||||
|
||||
if (backup == null)
|
||||
{
|
||||
TempData["Error"] = "Invalid backup file format";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
var success = await _contactService.RestoreContactsFromBackupAsync(toBotId, backup);
|
||||
|
||||
if (success)
|
||||
{
|
||||
TempData["Success"] = $"Successfully restored {backup.ContactCount} contacts to bot";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = "Failed to restore contacts from backup";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to restore backup");
|
||||
TempData["Error"] = "An error occurred while restoring the backup";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Admin/BotRecovery/ContactHistory/{telegramUserId}
|
||||
public async Task<IActionResult> ContactHistory(long telegramUserId)
|
||||
{
|
||||
// Show all bot interactions for a specific user
|
||||
ViewData["UserId"] = telegramUserId;
|
||||
// Implementation would query all BotContacts for this user across all bots
|
||||
return View();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user