- Add admin PWA push notifications for order management
- Integrate TeleBot customer messaging service
- Add push notification endpoints and VAPID key support
- Implement order status notifications throughout workflow
- Add notification UI components in admin panel
- Create TeleBotMessagingService for customer updates
- Add WebPush configuration to appsettings
- Fix compilation issues (BotStatus, BotContacts DbSet)
- Add comprehensive testing documentation
Features:
- Real-time admin notifications for new orders and status changes
- Customer order progress updates via TeleBot
- Graceful failure handling for notification services
- Test endpoints for notification system validation
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
165 lines
5.7 KiB
C#
165 lines
5.7 KiB
C#
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.Deleted);
|
|
|
|
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();
|
|
}
|
|
} |