using System.Text.Json; using Microsoft.EntityFrameworkCore; using eprintServer.Entities; using eprintServer.Services; namespace eprintServer.Data.Seed { public static class DbInitializer { public static async Task InitializeDbAsync(this IApplicationBuilder app) { using (var scope = app.ApplicationServices.CreateScope()) { var services = scope.ServiceProvider; var logger = services.GetRequiredService>(); try { var context = services.GetRequiredService(); var userService = services.GetRequiredService(); var generalLogger = services.GetRequiredService>(); logger.LogInformation("--> Applying database migrations..."); await context.Database.MigrateAsync(); await SeedLocalizationDataAsync(context, generalLogger); await SeedCurrenciesAsync(context, generalLogger); await SeedAdminUserAsync(userService, logger); await SeedAdminTestAsync(userService, logger); } catch (Exception ex) { logger.LogError(ex, "An error occurred during database initialization."); } } } private static async Task SeedAdminUserAsync(UserService userService, ILogger logger) { const string adminUsername = "Eprint Admin"; if (!await userService.DoesUsernameExistAsync(adminUsername)) { logger.LogInformation("--> Seeding admin user..."); var adminUser = new User { Username = adminUsername, Email = "admin@printstudio.com", Password = "admin123", Name = "admin eprint", PhoneNumber = "93831879", Active = true, Deleted = false, }; await userService.SaveAdminAsync(adminUser); logger.LogInformation("--> Admin user created successfully."); } else { logger.LogInformation("--> Admin user already exists. Seeding skipped."); } } private static async Task SeedAdminTestAsync(UserService userService, ILogger logger) { const string adminUsername = "Eprint Admin Test"; if (!await userService.DoesUsernameExistAsync(adminUsername)) { logger.LogInformation("--> Seeding admin user..."); var adminUser = new User { Username = adminUsername, Email = "admin@camel-soft.com", Password = "aze", Name = "admin eprint test", PhoneNumber = "93000000", Active = true, Deleted = false, }; await userService.SaveAdminAsync(adminUser); logger.LogInformation("--> Admin test created successfully."); } else { logger.LogInformation("--> Admin test already exists. Seeding skipped."); } } public static async Task SeedLocalizationDataAsync(DataContext context, ILogger logger) { var mandatoryLanguageTags = new List { "en", "ar" }; await SynchronizeLanguagesFromJson(context, logger, mandatoryLanguageTags); await SeedTranslationSetting(context, logger, mandatoryLanguageTags); } private static async Task SynchronizeLanguagesFromJson(DataContext context, ILogger logger, List mandatoryTags) { logger.LogInformation("--> Checking Data/languages.json for new languages..."); var existingTags = await context.Languages.Select(l => l.Tag).ToListAsync(); var jsonPath = "Data/languages.json"; if (!File.Exists(jsonPath)) { logger.LogWarning($"Languages file not found at {jsonPath}. Skipping JSON sync."); return; } var jsonContent = await File.ReadAllTextAsync(jsonPath); var jsonLanguages = JsonSerializer.Deserialize>(jsonContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (jsonLanguages == null) { logger.LogError("Failed to deserialize languages.json."); return; } var languagesToAdd = new List(); foreach (var lang in jsonLanguages) { if (!existingTags.Contains(lang.Tag)) { languagesToAdd.Add(lang); } } foreach (var tag in mandatoryTags) { if (!existingTags.Contains(tag) && !languagesToAdd.Any(l => l.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase))) { var missingLang = jsonLanguages.FirstOrDefault(l => l.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase)) ?? new Language { Tag = tag, Name = tag == "en" ? "English" : "Arabic" }; languagesToAdd.Add(missingLang); logger.LogWarning($"Mandatory language tag '{tag}' was missing and added for seeding."); } } if (languagesToAdd.Any()) { context.Languages.AddRange(languagesToAdd); try { await context.SaveChangesAsync(); logger.LogInformation($"Successfully added {languagesToAdd.Count} new languages."); } catch (DbUpdateException ex) { logger.LogError(ex, "Failed to synchronize languages from JSON. DbUpdateException."); throw; } } else { logger.LogInformation("No new languages to add."); } } private static async Task SeedTranslationSetting(DataContext context, ILogger logger, List mandatoryTags) { var settingExists = await context.TranslationSettings.AnyAsync(); if (!settingExists) { logger.LogInformation("--> Seeding initial TranslationSetting with mandatory tags (en, ar)..."); var availableTags = await context.Languages .Where(l => mandatoryTags.Contains(l.Tag)) .Select(l => l.Tag) .ToListAsync(); if (!availableTags.Any() || availableTags.Count < mandatoryTags.Count) { logger.LogError("Critical: Mandatory language tags ('en', 'ar') were not found in the database. Cannot seed TranslationSetting."); return; } var translationSetting = new TranslationSetting { Id = 1, ActiveLanguageTags = availableTags }; context.TranslationSettings.Add(translationSetting); try { await context.SaveChangesAsync(); logger.LogInformation("Successfully added initial TranslationSetting."); } catch (DbUpdateException ex) { logger.LogError(ex, "Failed to seed TranslationSetting. DbUpdateException."); throw; } } else { logger.LogInformation("TranslationSetting already exists."); } } // Retaining the empty method from the previous combined code structure private static async Task SeedUsers(DataContext context, ILogger logger) { if (await context.Users.AnyAsync()) return; } private static async Task SeedCurrenciesAsync(DataContext context, ILogger logger) { // Check if any currency already exists to prevent duplicate seeding if (await context.Currencies.AnyAsync()) { logger.LogInformation("--> Currencies already seeded. Skipping."); return; } logger.LogInformation("--> Seeding currencies from Data/currencies.json..."); var jsonPath = "Data/currencies.json"; if (!File.Exists(jsonPath)) { logger.LogError($"Currencies file not found at {jsonPath}. Skipping currency seed."); return; } try { var jsonContent = await File.ReadAllTextAsync(jsonPath); // The fix (JsonPropertyName attribute) in the Currency entity handles the mapping here var currencyWrapper = System.Text.Json.JsonSerializer.Deserialize(jsonContent, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); var currenciesToSeed = currencyWrapper?.Currencies; if (currenciesToSeed != null && currenciesToSeed.Any()) { // Set USD as default just as an example var defaultCurrency = currenciesToSeed.FirstOrDefault(c => c.Code == "USD"); if (defaultCurrency != null) { defaultCurrency.IsDefault = true; } context.Currencies.AddRange(currenciesToSeed); await context.SaveChangesAsync(); logger.LogInformation($"Successfully added {currenciesToSeed.Count} currencies."); } else { logger.LogWarning("No currencies found in JSON file to seed."); } } catch (Exception ex) { logger.LogError(ex, "Failed to seed currencies from JSON."); } } } }