Refactor payment verification to manual workflow and add comprehensive cleanup tools

Major changes:
• Remove BTCPay Server integration in favor of SilverPAY manual verification
• Add test data cleanup mechanisms (API endpoints and shell scripts)
• Fix compilation errors in TestController (IdentityReference vs CustomerIdentity)
• Add deployment automation scripts for Hostinger VPS
• Enhance integration testing with comprehensive E2E validation
• Add Blazor components and mobile-responsive CSS for admin interface
• Create production environment configuration scripts

Key Features Added:
• Manual payment verification through Admin panel Order Details
• Bulk test data cleanup with proper cascade handling
• Deployment automation with systemd service configuration
• Comprehensive E2E testing suite with SilverPAY integration validation
• Mobile-first admin interface improvements

Security & Production:
• Environment variable configuration for production secrets
• Proper JWT and VAPID key management
• SilverPAY API integration with live credentials
• Database cleanup and maintenance tools

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-25 19:29:00 +01:00
parent 1588c79df0
commit 127be759c8
46 changed files with 3470 additions and 971 deletions

View File

@@ -98,28 +98,28 @@ public class TestController : ControllerBase
{
// Get count before cleanup
var totalBots = await _context.Bots.CountAsync();
// Keep only the most recent active bot per platform
var keepBots = await _context.Bots
.Where(b => b.IsActive && b.Status == Enums.BotStatus.Active)
.GroupBy(b => b.PlatformId)
.Select(g => g.OrderByDescending(b => b.LastSeenAt ?? b.CreatedAt).First())
.ToListAsync();
var keepBotIds = keepBots.Select(b => b.Id).ToList();
// Delete old/inactive bots and related data
var botsToDelete = await _context.Bots
.Where(b => !keepBotIds.Contains(b.Id))
.ToListAsync();
_context.Bots.RemoveRange(botsToDelete);
await _context.SaveChangesAsync();
var deletedCount = botsToDelete.Count;
var remainingCount = keepBots.Count;
return Ok(new {
return Ok(new {
message = "Bot cleanup completed",
totalBots = totalBots,
deletedBots = deletedCount,
@@ -138,4 +138,117 @@ public class TestController : ControllerBase
return BadRequest(new { error = ex.Message });
}
}
[HttpPost("cleanup-test-data")]
public async Task<IActionResult> CleanupTestData()
{
try
{
// Get counts before cleanup
var totalOrders = await _context.Orders.CountAsync();
var totalCryptoPayments = await _context.CryptoPayments.CountAsync();
var totalOrderItems = await _context.OrderItems.CountAsync();
// Find test orders (identity references starting with "test-")
var testOrders = await _context.Orders
.Where(o => o.IdentityReference != null && o.IdentityReference.StartsWith("test-"))
.ToListAsync();
var testOrderIds = testOrders.Select(o => o.Id).ToList();
// Remove crypto payments for test orders
var cryptoPaymentsToDelete = await _context.CryptoPayments
.Where(cp => testOrderIds.Contains(cp.OrderId))
.ToListAsync();
// Remove order items for test orders
var orderItemsToDelete = await _context.OrderItems
.Where(oi => testOrderIds.Contains(oi.OrderId))
.ToListAsync();
// Delete all related data
_context.CryptoPayments.RemoveRange(cryptoPaymentsToDelete);
_context.OrderItems.RemoveRange(orderItemsToDelete);
_context.Orders.RemoveRange(testOrders);
await _context.SaveChangesAsync();
// Get counts after cleanup
var remainingOrders = await _context.Orders.CountAsync();
var remainingCryptoPayments = await _context.CryptoPayments.CountAsync();
var remainingOrderItems = await _context.OrderItems.CountAsync();
return Ok(new {
message = "Test data cleanup completed",
before = new {
orders = totalOrders,
cryptoPayments = totalCryptoPayments,
orderItems = totalOrderItems
},
after = new {
orders = remainingOrders,
cryptoPayments = remainingCryptoPayments,
orderItems = remainingOrderItems
},
deleted = new {
orders = testOrders.Count,
cryptoPayments = cryptoPaymentsToDelete.Count,
orderItems = orderItemsToDelete.Count
},
testOrdersFound = testOrders.Select(o => new {
id = o.Id,
identityReference = o.IdentityReference,
createdAt = o.CreatedAt,
total = o.Total
})
});
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
[HttpGet("database")]
public async Task<IActionResult> DatabaseHealthCheck()
{
try
{
// Test database connectivity by executing a simple query
var canConnect = await _context.Database.CanConnectAsync();
if (!canConnect)
{
return StatusCode(503, new {
status = "unhealthy",
message = "Cannot connect to database",
timestamp = DateTime.UtcNow
});
}
// Test actual query execution
var categoryCount = await _context.Categories.CountAsync();
var productCount = await _context.Products.CountAsync();
var orderCount = await _context.Orders.CountAsync();
return Ok(new {
status = "healthy",
message = "Database connection successful",
stats = new {
categories = categoryCount,
products = productCount,
orders = orderCount
},
timestamp = DateTime.UtcNow
});
}
catch (Exception ex)
{
return StatusCode(503, new {
status = "unhealthy",
error = ex.Message,
timestamp = DateTime.UtcNow
});
}
}
}