BTCPay-infrastructure-recovery
This commit is contained in:
@@ -103,6 +103,21 @@ public class AuthService : IAuthService
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<UserDto?> GetUserByUsernameAsync(string username)
|
||||
{
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Username == username && u.IsActive);
|
||||
if (user == null) return null;
|
||||
|
||||
return new UserDto
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
CreatedAt = user.CreatedAt,
|
||||
IsActive = user.IsActive
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserDto>> GetAllUsersAsync()
|
||||
{
|
||||
return await _context.Users
|
||||
|
||||
@@ -23,11 +23,33 @@ public class DataSeederService : IDataSeederService
|
||||
|
||||
public async Task SeedSampleDataAsync()
|
||||
{
|
||||
// Check if we already have data
|
||||
var hasCategories = await _context.Categories.AnyAsync();
|
||||
if (hasCategories)
|
||||
await SeedProductionDataAsync();
|
||||
}
|
||||
|
||||
private async Task SeedProductionDataAsync()
|
||||
{
|
||||
_logger.LogInformation("Setting up production-ready catalog...");
|
||||
|
||||
// Clean up existing test products first (excluding valid products that just need stock update)
|
||||
var testProducts = await _context.Products
|
||||
.Where(p => p.Name.Contains("JAMES") || p.Name.Contains("dsasada") || p.Name.Contains("asdsads"))
|
||||
.ToListAsync();
|
||||
|
||||
if (testProducts.Any())
|
||||
{
|
||||
_logger.LogInformation("Sample data already exists, skipping seed");
|
||||
_context.Products.RemoveRange(testProducts);
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInformation("Removed {Count} test products", testProducts.Count);
|
||||
}
|
||||
|
||||
// Check if we need to create production catalog or update stock
|
||||
var hasProductionProducts = await _context.Products
|
||||
.AnyAsync(p => p.Name.Contains("Wireless Noise-Cancelling Headphones"));
|
||||
|
||||
if (hasProductionProducts)
|
||||
{
|
||||
// Update stock for existing production products
|
||||
await UpdateProductionStockAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,75 +91,161 @@ public class DataSeederService : IDataSeederService
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInformation("Created {Count} categories", categories.Count);
|
||||
|
||||
// Create Products
|
||||
// Ensure we have categories before creating products
|
||||
var electronicsCategory = await _context.Categories.FirstOrDefaultAsync(c => c.Name == "Electronics");
|
||||
var clothingCategory = await _context.Categories.FirstOrDefaultAsync(c => c.Name == "Clothing");
|
||||
var booksCategory = await _context.Categories.FirstOrDefaultAsync(c => c.Name == "Books");
|
||||
|
||||
if (electronicsCategory == null || clothingCategory == null || booksCategory == null)
|
||||
{
|
||||
_logger.LogWarning("Categories not found, creating them first");
|
||||
// Categories would be created by the original seeder logic above
|
||||
}
|
||||
|
||||
// Create Production-Ready Products with proper stock
|
||||
var products = new List<Product>
|
||||
{
|
||||
// ELECTRONICS - High-margin, popular items
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Wireless Headphones",
|
||||
Description = "High-quality Bluetooth headphones with noise cancellation",
|
||||
Price = 89.99m,
|
||||
Weight = 250,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 10,
|
||||
CategoryId = categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Smartphone Case",
|
||||
Description = "Durable protective case for latest smartphones",
|
||||
Price = 19.99m,
|
||||
Weight = 50,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 10,
|
||||
CategoryId = categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "T-Shirt",
|
||||
Description = "100% cotton comfortable t-shirt",
|
||||
Price = 24.99m,
|
||||
Weight = 200,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 15,
|
||||
CategoryId = categories[1].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Jeans",
|
||||
Description = "Classic denim jeans",
|
||||
Price = 59.99m,
|
||||
Weight = 500,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 15,
|
||||
CategoryId = categories[1].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Programming Book",
|
||||
Description = "Learn programming with practical examples",
|
||||
Price = 34.99m,
|
||||
Weight = 800,
|
||||
Name = "Wireless Noise-Cancelling Headphones",
|
||||
Description = "Premium Bluetooth 5.0 headphones with active noise cancellation, 30-hour battery life, and crystal-clear audio. Perfect for music, calls, and travel. Includes carrying case and charging cable.",
|
||||
Price = 149.99m,
|
||||
Weight = 280,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 25,
|
||||
CategoryId = categories[2].Id,
|
||||
CategoryId = electronicsCategory?.Id ?? categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Fast Wireless Charging Stand",
|
||||
Description = "15W fast wireless charger compatible with iPhone, Samsung, and Qi-enabled devices. Anti-slip base, LED indicator, includes AC adapter. Charge through most phone cases up to 5mm thick.",
|
||||
Price = 34.99m,
|
||||
Weight = 180,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 50,
|
||||
CategoryId = electronicsCategory?.Id ?? categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Ultra-Slim Power Bank 20,000mAh",
|
||||
Description = "High-capacity portable charger with dual USB-A and USB-C ports. Fast charging technology, digital display shows remaining power. Charges iPhone 13 up to 4 times, includes USB-C cable.",
|
||||
Price = 59.99m,
|
||||
Weight = 450,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 35,
|
||||
CategoryId = electronicsCategory?.Id ?? categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Premium Phone Case with MagSafe",
|
||||
Description = "Military-grade protection with built-in MagSafe compatibility. Drop-tested to 12 feet, raised camera and screen edges, clear back shows your phone's design. Compatible with iPhone 14/15 series.",
|
||||
Price = 29.99m,
|
||||
Weight = 65,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 75,
|
||||
CategoryId = electronicsCategory?.Id ?? categories[0].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
|
||||
// CLOTHING - Essential wardrobe items
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Premium Cotton T-Shirt",
|
||||
Description = "100% organic cotton, pre-shrunk, tagless design. Soft, breathable fabric in classic fit. Available in multiple colors. Perfect for casual wear or layering. Machine washable, retains shape after washing.",
|
||||
Price = 24.99m,
|
||||
Weight = 180,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 100,
|
||||
CategoryId = clothingCategory?.Id ?? categories[1].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Classic Denim Jeans",
|
||||
Description = "Premium denim with perfect stretch for comfort. Classic 5-pocket styling, reinforced stress points, fade-resistant color. Available in multiple washes and sizes. Timeless style that works with everything.",
|
||||
Price = 79.99m,
|
||||
Weight = 650,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 60,
|
||||
CategoryId = clothingCategory?.Id ?? categories[1].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Cozy Knit Sweater",
|
||||
Description = "Soft merino wool blend, lightweight yet warm. Crew neck design, ribbed cuffs and hem. Perfect for layering or wearing alone. Hand-washable, pill-resistant fabric maintains shape and softness.",
|
||||
Price = 89.99m,
|
||||
Weight = 320,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 40,
|
||||
CategoryId = clothingCategory?.Id ?? categories[1].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
|
||||
// BOOKS - Knowledge and entertainment
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "The Complete Guide to Cryptocurrency",
|
||||
Description = "Comprehensive guide to understanding Bitcoin, Ethereum, DeFi, and blockchain technology. Written for beginners and enthusiasts. 400+ pages with real-world examples, investment strategies, and security tips.",
|
||||
Price = 39.99m,
|
||||
Weight = 580,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 30,
|
||||
CategoryId = booksCategory?.Id ?? categories[2].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Modern Web Development Handbook",
|
||||
Description = "Learn React, Node.js, and modern JavaScript. Hands-on projects, best practices, and deployment strategies. Includes access to online code repository and video tutorials. Perfect for career advancement.",
|
||||
Price = 49.99m,
|
||||
Weight = 720,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 25,
|
||||
CategoryId = booksCategory?.Id ?? categories[2].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Mindfulness and Productivity Journal",
|
||||
Description = "Daily planner with mindfulness exercises and productivity techniques. 6-month undated format, premium paper, goal-setting frameworks. Improve focus, reduce stress, achieve work-life balance.",
|
||||
Price = 27.99m,
|
||||
Weight = 380,
|
||||
WeightUnit = ProductWeightUnit.Grams,
|
||||
StockQuantity = 45,
|
||||
CategoryId = booksCategory?.Id ?? categories[2].Id,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
@@ -488,4 +596,44 @@ public class DataSeederService : IDataSeederService
|
||||
|
||||
_logger.LogInformation("Sample data seeding completed successfully!");
|
||||
}
|
||||
|
||||
private async Task UpdateProductionStockAsync()
|
||||
{
|
||||
_logger.LogInformation("Updating production product stock levels...");
|
||||
|
||||
var productStockUpdates = new Dictionary<string, int>
|
||||
{
|
||||
["Wireless Noise-Cancelling Headphones"] = 25,
|
||||
["Fast Wireless Charging Stand"] = 50,
|
||||
["Ultra-Slim Power Bank 20,000mAh"] = 35,
|
||||
["Premium Phone Case with MagSafe"] = 75,
|
||||
["Premium Cotton T-Shirt"] = 100,
|
||||
["Classic Denim Jeans"] = 60,
|
||||
["Cozy Knit Sweater"] = 40,
|
||||
["The Complete Guide to Cryptocurrency"] = 30,
|
||||
["Modern Web Development Handbook"] = 25,
|
||||
["Mindfulness and Productivity Journal"] = 45
|
||||
};
|
||||
|
||||
foreach (var update in productStockUpdates)
|
||||
{
|
||||
var product = await _context.Products
|
||||
.FirstOrDefaultAsync(p => p.Name == update.Key);
|
||||
|
||||
if (product != null)
|
||||
{
|
||||
var oldStock = product.StockQuantity;
|
||||
product.StockQuantity = update.Value;
|
||||
product.UpdatedAt = DateTime.UtcNow;
|
||||
_logger.LogInformation("Updated stock for {Product} from {OldStock} to {NewStock}", product.Name, oldStock, update.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Product not found: {ProductName}", update.Key);
|
||||
}
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInformation("Production stock update completed!");
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ public interface IAuthService
|
||||
Task<bool> SeedDefaultUserAsync();
|
||||
Task<UserDto?> CreateUserAsync(CreateUserDto createUserDto);
|
||||
Task<UserDto?> GetUserByIdAsync(Guid id);
|
||||
Task<UserDto?> GetUserByUsernameAsync(string username);
|
||||
Task<IEnumerable<UserDto>> GetAllUsersAsync();
|
||||
Task<bool> DeleteUserAsync(Guid id);
|
||||
Task<bool> UpdateUserAsync(Guid id, UpdateUserDto updateUserDto);
|
||||
|
||||
@@ -30,6 +30,7 @@ public class ProductService : IProductService
|
||||
Price = p.Price,
|
||||
Weight = p.Weight,
|
||||
WeightUnit = p.WeightUnit,
|
||||
StockQuantity = p.StockQuantity,
|
||||
CategoryId = p.CategoryId,
|
||||
CategoryName = p.Category.Name,
|
||||
CreatedAt = p.CreatedAt,
|
||||
@@ -61,6 +62,7 @@ public class ProductService : IProductService
|
||||
Price = p.Price,
|
||||
Weight = p.Weight,
|
||||
WeightUnit = p.WeightUnit,
|
||||
StockQuantity = p.StockQuantity,
|
||||
CategoryId = p.CategoryId,
|
||||
CategoryName = p.Category.Name,
|
||||
CreatedAt = p.CreatedAt,
|
||||
@@ -309,6 +311,7 @@ public class ProductService : IProductService
|
||||
Price = p.Price,
|
||||
Weight = p.Weight,
|
||||
WeightUnit = p.WeightUnit,
|
||||
StockQuantity = p.StockQuantity,
|
||||
CategoryId = p.CategoryId,
|
||||
CategoryName = p.Category.Name,
|
||||
CreatedAt = p.CreatedAt,
|
||||
|
||||
@@ -40,6 +40,14 @@ public class PushNotificationService : IPushNotificationService
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the user actually exists in the database
|
||||
var userExists = await _context.Users.AnyAsync(u => u.Id == userId);
|
||||
if (!userExists)
|
||||
{
|
||||
Log.Warning("Attempted to subscribe non-existent user {UserId} to push notifications", userId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if subscription already exists
|
||||
var existingSubscription = await _context.PushSubscriptions
|
||||
.FirstOrDefaultAsync(ps => ps.Endpoint == subscriptionDto.Endpoint && ps.UserId == userId);
|
||||
@@ -53,6 +61,7 @@ public class PushNotificationService : IPushNotificationService
|
||||
existingSubscription.IsActive = true;
|
||||
existingSubscription.UserAgent = userAgent;
|
||||
existingSubscription.IpAddress = ipAddress;
|
||||
Log.Information("Updated existing push subscription for user {UserId}", userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -71,10 +80,11 @@ public class PushNotificationService : IPushNotificationService
|
||||
};
|
||||
|
||||
_context.PushSubscriptions.Add(subscription);
|
||||
Log.Information("Created new push subscription for user {UserId}", userId);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
Log.Information("Push subscription created/updated for user {UserId}", userId);
|
||||
Log.Information("Push subscription saved successfully for user {UserId}", userId);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user