diff --git a/FruitBank.sln b/FruitBank.sln
index 84afd68..4220d61 100644
--- a/FruitBank.sln
+++ b/FruitBank.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.32002.185
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11222.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{E4ACA93B-D3DE-4557-B069-F1DB42925A4B}"
EndProject
@@ -87,6 +87,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nop.Plugin.Misc.MangoCore",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Nop.Data", "..\NopCommerce.Common\4.70\Libraries\Mango.Nop.Data\Mango.Nop.Data.csproj", "{EE44B558-F1DA-433A-BD4C-D275986A4679}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Sandbox.EndPoints", "Tests\Mango.Sandbox\Mango.Sandbox.EndPoints\Mango.Sandbox.EndPoints.csproj", "{D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Sandbox.EndPoints.Tests", "Tests\Mango.Sandbox\Mango.Sandbox.EndPoints.Tests\Mango.Sandbox.EndPoints.Tests.csproj", "{B8491E5C-DBB5-1594-052E-744D78D7A4DE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Sandbox.SignalRTestClient", "Tests\Mango.Sandbox\Mango.Sandbox.SignalRTestClient\Mango.Sandbox.SignalRTestClient.csproj", "{B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -507,6 +513,42 @@ Global
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|x86.ActiveCfg = Release|Any CPU
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|x86.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|x86.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|x86.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|x86.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|x86.Build.0 = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Debug|x86.Build.0 = Debug|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|x86.ActiveCfg = Release|Any CPU
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -550,6 +592,9 @@ Global
{3E893AC2-29F1-48FC-B33F-F73C6EE2BE90} = {0742FDF3-0F2E-4C64-9521-E58A7FF2ED26}
{3976CB1D-8080-4B84-8C01-1F98BFCAF2B3} = {0742FDF3-0F2E-4C64-9521-E58A7FF2ED26}
{EE44B558-F1DA-433A-BD4C-D275986A4679} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C} = {E8FC6874-E230-468A-9685-4747354B92FF}
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE} = {E8FC6874-E230-468A-9685-4747354B92FF}
+ {B2FBDB33-24F4-E0B1-0EA7-7939A387F88F} = {E8FC6874-E230-468A-9685-4747354B92FF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EE72A8B2-332A-4175-9319-6726D36E9D25}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj
new file mode 100644
index 0000000..0c2b8e2
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net9.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalREndpointTests.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalREndpointTests.cs
new file mode 100644
index 0000000..76a7aa6
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalREndpointTests.cs
@@ -0,0 +1,218 @@
+using Microsoft.AspNetCore.SignalR.Client;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Diagnostics;
+using System.Text;
+using System.Text.Json;
+
+namespace Mango.Sandbox.EndPoints.Tests;
+
+///
+/// SignalR Endpoint tesztek.
+/// FONTOS: A SANDBOX-ot manuálisan kell elindítani a tesztek futtatása előtt!
+// Indítás: dotnet run --project Mango.Sandbox.EndPoints --urls http://localhost:59579
+///
+[TestClass]
+public class SignalREndpointTests
+{
+ private static readonly string SandboxUrl = "http://localhost:59579";
+ private static readonly string HubUrl = $"{SandboxUrl}/fbHub";
+
+ // SignalR Tags from FruitBank.Common.SignalRs.SignalRTags
+ private const int GetMeasuringUsersTag = 70;
+ private const int GetStockQuantityHistoryDtosTag = 40;
+ private const int GetStockQuantityHistoryDtosByProductIdTag = 41;
+ private const int GetShippingDocumentsByShippingIdTag = 60;
+ private const int GetOrderDtoByIdTag = 21;
+ private const int GetStockTakingItemsByIdTag = 81;
+
+ [ClassInitialize]
+ public static async Task ClassInitialize(TestContext context)
+ {
+ using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
+ try
+ {
+ var response = await httpClient.GetAsync($"{SandboxUrl}/health");
+ Assert.IsTrue(response.IsSuccessStatusCode,
+ "SANDBOX is not running! Start it manually: dotnet run --project Mango.Sandbox.EndPoints --urls http://localhost:59579");
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail($"SANDBOX is not running! Error: {ex.Message}\n" +
+ "Start it manually: dotnet run --project Mango.Sandbox.EndPoints --urls http://localhost:59579");
+ }
+ }
+
+ #region HTTP Endpoint Tests
+
+ [TestMethod]
+ public async Task HealthEndpoint_ReturnsSuccess()
+ {
+ using var httpClient = new HttpClient();
+ var response = await httpClient.GetAsync($"{SandboxUrl}/health");
+ Assert.IsTrue(response.IsSuccessStatusCode, $"Health endpoint returned {response.StatusCode}");
+ }
+
+ [TestMethod]
+ public async Task RootEndpoint_ReturnsSandboxIsRunning()
+ {
+ using var httpClient = new HttpClient();
+ var response = await httpClient.GetStringAsync(SandboxUrl);
+ Assert.AreEqual("SANDBOX is running!", response);
+ }
+
+ #endregion
+
+ #region SignalR Connection Tests
+
+ [TestMethod]
+ public async Task SignalR_Negotiate_ReturnsSuccess()
+ {
+ using var httpClient = new HttpClient();
+ var response = await httpClient.PostAsync($"{HubUrl}/negotiate?negotiateVersion=1", null);
+ Assert.IsTrue(response.IsSuccessStatusCode, $"SignalR negotiate returned {response.StatusCode}");
+ }
+
+ [TestMethod]
+ public async Task SignalR_Connect_Succeeds()
+ {
+ var connection = new HubConnectionBuilder()
+ .WithUrl(HubUrl)
+ .Build();
+
+ try
+ {
+ await connection.StartAsync();
+ Assert.AreEqual(HubConnectionState.Connected, connection.State);
+ }
+ finally
+ {
+ await connection.StopAsync();
+ }
+ }
+
+ #endregion
+
+ #region SignalR Business Endpoint Tests
+
+ [TestMethod]
+ public async Task SignalR_GetMeasuringUsers_ReturnsJson()
+ {
+ await TestSignalREndpoint(GetMeasuringUsersTag, null, "GetMeasuringUsers");
+ }
+
+ [TestMethod]
+ public async Task SignalR_GetStockQuantityHistoryDtos_ReturnsJson()
+ {
+ await TestSignalREndpoint(GetStockQuantityHistoryDtosTag, null, "GetStockQuantityHistoryDtos");
+ }
+
+ [TestMethod]
+ public async Task SignalR_GetStockQuantityHistoryDtosByProductId_ReturnsJson()
+ {
+ // ProductId = 10
+ await TestSignalREndpoint(GetStockQuantityHistoryDtosByProductIdTag, 10, "GetStockQuantityHistoryDtosByProductId");
+ }
+
+ [TestMethod]
+ public async Task SignalR_GetShippingDocumentsByShippingId_ReturnsJson()
+ {
+ // ShippingId = 5
+ await TestSignalREndpoint(GetShippingDocumentsByShippingIdTag, 5, "GetShippingDocumentsByShippingId");
+ }
+
+ [TestMethod]
+ public async Task SignalR_GetOrderDtoById_ReturnsJson()
+ {
+ // OrderId = 15
+ await TestSignalREndpoint(GetOrderDtoByIdTag, 15, "GetOrderDtoById");
+ }
+
+ [TestMethod]
+ public async Task SignalR_GetStockTakingItemsById_ReturnsJson()
+ {
+ // StockTakingItemId = 200
+ await TestSignalREndpoint(GetStockTakingItemsByIdTag, 200, "GetStockTakingItemsById");
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ private async Task TestSignalREndpoint(int tag, object? parameter, string endpointName)
+ {
+ var connection = new HubConnectionBuilder()
+ .WithUrl(HubUrl)
+ .Build();
+
+ string? receivedJson = null;
+ int receivedTag = -1;
+ var responseReceived = new TaskCompletionSource();
+
+ connection.On("ReceiveMessage", (responseTag, data) =>
+ {
+ receivedTag = responseTag;
+ if (data != null && data.Length > 0)
+ {
+ receivedJson = Encoding.UTF8.GetString(data);
+ }
+ responseReceived.TrySetResult(true);
+ });
+
+ try
+ {
+ await connection.StartAsync();
+ Assert.AreEqual(HubConnectionState.Connected, connection.State, $"Failed to connect to SignalR hub for {endpointName}");
+
+ // Készítsük el a request data-t
+ byte[] requestData = parameter != null
+ ? Encoding.UTF8.GetBytes(JsonSerializer.Serialize(parameter))
+ : Array.Empty();
+
+ await connection.InvokeAsync("ReceiveMessage", tag, requestData);
+
+ var completed = await Task.WhenAny(responseReceived.Task, Task.Delay(15000));
+
+ if (completed == responseReceived.Task)
+ {
+ Console.WriteLine($"[{endpointName}] Response tag: {receivedTag}");
+ Console.WriteLine($"[{endpointName}] Response JSON: {receivedJson?.Substring(0, Math.Min(500, receivedJson?.Length ?? 0))}...");
+
+ // Ellenőrizzük, hogy valid JSON-e (ha van adat)
+ if (!string.IsNullOrEmpty(receivedJson))
+ {
+ try
+ {
+ var jsonDoc = JsonDocument.Parse(receivedJson);
+ Assert.IsTrue(
+ jsonDoc.RootElement.ValueKind == JsonValueKind.Array ||
+ jsonDoc.RootElement.ValueKind == JsonValueKind.Object ||
+ jsonDoc.RootElement.ValueKind == JsonValueKind.Null,
+ $"[{endpointName}] Response is not a valid JSON");
+ }
+ catch (JsonException ex)
+ {
+ Assert.Fail($"[{endpointName}] Invalid JSON response: {ex.Message}");
+ }
+ }
+ }
+ else
+ {
+ Assert.AreEqual(HubConnectionState.Connected, connection.State,
+ $"[{endpointName}] Connection was closed - check SANDBOX logs for DI errors");
+ }
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail($"[{endpointName}] SignalR error: {ex.Message}. Check SANDBOX logs for missing DI registrations.");
+ }
+ finally
+ {
+ if (connection.State == HubConnectionState.Connected)
+ {
+ await connection.StopAsync();
+ }
+ }
+ }
+
+ #endregion
+}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj
new file mode 100644
index 0000000..7337993
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj
@@ -0,0 +1,144 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+ false
+
+ $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..\..'))\
+ $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..\..\..'))\
+ $(FruitBankRoot)Presentation\Nop.Web\Plugins\Misc.FruitBankPlugin\
+ $(SourceRoot)FruitBankHybridApp\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(PluginOutputDir)Nop.Plugin.Misc.FruitBankPlugin.dll
+
+
+ $(PluginOutputDir)Mango.Nop.Core.dll
+
+
+ $(PluginOutputDir)Mango.Nop.Services.dll
+
+
+
+
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Core.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Core.Server.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Database.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Entities.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Entities.Server.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Interfaces.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Interfaces.Server.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Models.Server.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Services.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Services.Server.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Utils.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.Server.dll
+
+
+
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs
new file mode 100644
index 0000000..c674ff9
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs
@@ -0,0 +1,739 @@
+using AyCode.Core.Loggers;
+using FluentValidation;
+using FluentValidation.AspNetCore;
+using FruitBank.Common;
+using FruitBank.Common.Interfaces;
+using FruitBank.Common.Server.Interfaces;
+using FruitBank.Common.Server.Services.Loggers;
+using FruitBank.Common.Server.Services.SignalRs;
+using Mango.Nop.Services;
+using Mango.Nop.Services.Loggers;
+using Mango.Sandbox.EndPoints.Services;
+using Microsoft.AspNetCore.Http.Connections;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Nop.Core;
+using Nop.Core.Caching;
+using Nop.Core.Configuration;
+using Nop.Core.Domain;
+using Nop.Core.Domain.Blogs;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Common;
+using Nop.Core.Domain.Customers;
+using Nop.Core.Domain.Directory;
+using Nop.Core.Domain.Forums;
+using Nop.Core.Domain.Gdpr;
+using Nop.Core.Domain.Localization;
+using Nop.Core.Domain.Media;
+using Nop.Core.Domain.Messages;
+using Nop.Core.Domain.News;
+using Nop.Core.Domain.Orders;
+using Nop.Core.Domain.Payments;
+using Nop.Core.Domain.Security;
+using Nop.Core.Domain.Seo;
+using Nop.Core.Domain.Shipping;
+using Nop.Core.Domain.Stores;
+using Nop.Core.Domain.Tax;
+using Nop.Core.Domain.Vendors;
+using Nop.Core.Events;
+using Nop.Core.Http;
+using Nop.Core.Infrastructure;
+using Nop.Core.Security;
+using Nop.Data;
+using Nop.Data.DataProviders;
+using Nop.Data.Mapping;
+using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;
+using Nop.Plugin.Misc.FruitBankPlugin.Controllers;
+using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
+using Nop.Plugin.Misc.FruitBankPlugin.Factories;
+using Nop.Plugin.Misc.FruitBankPlugin.Mapping;
+using Nop.Plugin.Misc.FruitBankPlugin.Services;
+using Nop.Services.Affiliates;
+using Nop.Services.Attributes;
+using Nop.Services.Authentication;
+using Nop.Services.Authentication.External;
+using Nop.Services.Authentication.MultiFactor;
+using Nop.Services.Blogs;
+using Nop.Services.Caching;
+using Nop.Services.Catalog;
+using Nop.Services.Cms;
+using Nop.Services.Common;
+using Nop.Services.Configuration;
+using Nop.Services.Customers;
+using Nop.Services.Directory;
+using Nop.Services.Discounts;
+using Nop.Services.Events;
+using Nop.Services.ExportImport;
+using Nop.Services.Forums;
+using Nop.Services.Gdpr;
+using Nop.Services.Helpers;
+using Nop.Services.Html;
+using Nop.Services.Installation;
+using Nop.Services.Localization;
+using Nop.Services.Logging;
+using Nop.Services.Media;
+using Nop.Services.Media.RoxyFileman;
+using Nop.Services.Messages;
+using Nop.Services.News;
+using Nop.Services.Orders;
+using Nop.Services.Payments;
+using Nop.Services.Plugins;
+using Nop.Services.Plugins.Marketplace;
+using Nop.Services.Polls;
+using Nop.Services.ScheduleTasks;
+using Nop.Services.Security;
+using Nop.Services.Seo;
+using Nop.Services.Shipping;
+using Nop.Services.Shipping.Date;
+using Nop.Services.Shipping.Pickup;
+using Nop.Services.Stores;
+using Nop.Services.Tax;
+using Nop.Services.Themes;
+using Nop.Services.Topics;
+using Nop.Services.Vendors;
+using Nop.Web.Areas.Admin.Factories;
+using Nop.Web.Areas.Admin.Helpers;
+using Nop.Web.Framework;
+using Nop.Web.Framework.Factories;
+using Nop.Web.Framework.Infrastructure;
+using Nop.Web.Framework.Menu;
+using Nop.Web.Framework.Mvc.Routing;
+using Nop.Web.Framework.Themes;
+using Nop.Web.Framework.UI;
+using Nop.Web.Infrastructure.Installation;
+using System.Net.Http.Headers;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// ===========================================
+// === KONFIGURÁCIÓ ===
+// ===========================================
+
+var prodAppSettingsPath = Path.GetFullPath(Path.Combine(
+ builder.Environment.ContentRootPath,
+ "..", "..", "..",
+ "Presentation", "Nop.Web", "App_Data", "appsettings.json"));
+
+if (File.Exists(prodAppSettingsPath))
+{
+ builder.Configuration.AddJsonFile(prodAppSettingsPath, optional: false, reloadOnChange: true);
+ Console.WriteLine($"[SANDBOX] PROD config loaded: {prodAppSettingsPath}");
+}
+else
+{
+ Console.WriteLine($"[SANDBOX] WARNING: PROD appsettings.json not found at: {prodAppSettingsPath}");
+}
+
+builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
+Console.WriteLine("[SANDBOX] SANDBOX config overrides applied");
+
+// ===========================================
+// === NAME COMPATIBILITY - LinqToDB tábla mapping ===
+// ===========================================
+
+NameCompatibilityManager.AdditionalNameCompatibilities.Add(typeof(NameCompatibility));
+Console.WriteLine("[SANDBOX] FruitBank NameCompatibility registered for LinqToDB table mapping");
+
+// ===========================================
+// === FILE PROVIDER (STATIC) ===
+// ===========================================
+
+CommonHelper.DefaultFileProvider = new NopFileProvider(builder.Environment);
+
+// ===========================================
+// === TYPE FINDER ===
+// ===========================================
+
+var typeFinder = new WebAppTypeFinder();
+Singleton.Instance = typeFinder;
+builder.Services.AddSingleton(typeFinder);
+Console.WriteLine("[SANDBOX] TypeFinder registered");
+
+// ===========================================
+// === ENGINE ===
+// ===========================================
+
+var engine = new NopEngine();
+Singleton.Instance = engine;
+builder.Services.AddSingleton(engine);
+Console.WriteLine("[SANDBOX] NopEngine registered");
+
+// ===========================================
+// === CORS ===
+// ===========================================
+
+builder.Services.AddCors(options =>
+{
+ options.AddDefaultPolicy(policy =>
+ {
+ policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
+ });
+
+ options.AddPolicy("SignalR", policy =>
+ {
+ policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
+ });
+});
+
+// ===========================================
+// === DI VALIDÁCIÓ KIKAPCSOLÁSA ===
+// ===========================================
+
+builder.Host.UseDefaultServiceProvider(options =>
+{
+ options.ValidateScopes = false;
+ options.ValidateOnBuild = false;
+});
+
+// ===========================================
+// === MVC INFRASTRUKTÚRA ===
+// ===========================================
+
+builder.Services.AddControllersWithViews();
+builder.Services.AddRazorPages();
+builder.Services.AddSession();
+
+// ===========================================
+// === ALAPVETŐ INFRASTRUKTÚRA ===
+// ===========================================
+
+builder.Services.AddHttpContextAccessor();
+builder.Services.AddSingleton();
+builder.Services.AddMemoryCache();
+
+// ===========================================
+// === APP SETTINGS ===
+// ===========================================
+
+var appSettings = new AppSettings();
+builder.Configuration.Bind(appSettings);
+builder.Services.AddSingleton(appSettings);
+Singleton.Instance = appSettings;
+
+// ===========================================
+// === FILE PROVIDER (DI) ===
+// ===========================================
+
+builder.Services.AddScoped();
+
+// ===========================================
+// === ADATBÁZIS ÉS REPOSITORY ===
+// ===========================================
+
+builder.Services.AddScoped();
+builder.Services.AddScoped(typeof(IRepository<>), typeof(EntityRepository<>));
+
+// ===========================================
+// === CACHE SZOLGÁLTATÁSOK ===
+// ===========================================
+
+builder.Services.AddTransient(typeof(IConcurrentCollection<>), typeof(ConcurrentTrie<>));
+builder.Services.AddSingleton();
+builder.Services.AddScoped();
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+builder.Services.AddScoped();
+
+// ===========================================
+// === EVENT PUBLISHER ===
+// ===========================================
+
+builder.Services.AddSingleton();
+
+// ===========================================
+// === LAZY WRAPPERS ===
+// ===========================================
+
+builder.Services.AddScoped(typeof(Lazy<>), typeof(LazyInstance<>));
+
+// ===========================================
+// === NOP SETTINGS (Domain Settings) - DINAMIKUS REGISZTRÁCIÓ ===
+// ===========================================
+
+// Alapvető Settings-ek fix értékekkel (amik a SANDBOX működéséhez szükségesek)
+builder.Services.AddScoped(sp => new CookieSettings());
+builder.Services.AddScoped(sp => new CurrencySettings { PrimaryStoreCurrencyId = 1 });
+builder.Services.AddScoped(sp => new LocalizationSettings { DefaultAdminLanguageId = 1, AutomaticallyDetectLanguage = false });
+builder.Services.AddScoped(sp => new TaxSettings { TaxDisplayType = TaxDisplayType.IncludingTax });
+builder.Services.AddScoped(sp => new CatalogSettings());
+builder.Services.AddScoped(sp => new OrderSettings());
+builder.Services.AddScoped(sp => new ShippingSettings());
+builder.Services.AddScoped(sp => new RewardPointsSettings());
+builder.Services.AddScoped(sp => new CustomerSettings());
+builder.Services.AddScoped(sp => new CommonSettings());
+builder.Services.AddScoped(sp => new ShoppingCartSettings());
+builder.Services.AddScoped(sp => new MediaSettings());
+builder.Services.AddScoped(sp => new StoreInformationSettings());
+builder.Services.AddScoped(sp => new SeoSettings());
+builder.Services.AddScoped(sp => new SecuritySettings());
+builder.Services.AddScoped(sp => new AdminAreaSettings());
+builder.Services.AddScoped(sp => new EmailAccountSettings());
+builder.Services.AddScoped(sp => new MessagesSettings());
+builder.Services.AddScoped(sp => new ExternalAuthenticationSettings());
+builder.Services.AddScoped(sp => new VendorSettings());
+builder.Services.AddScoped(sp => new BlogSettings());
+builder.Services.AddScoped(sp => new NewsSettings());
+builder.Services.AddScoped(sp => new ForumSettings());
+builder.Services.AddScoped(sp => new GdprSettings());
+builder.Services.AddScoped(sp => new PaymentSettings());
+builder.Services.AddScoped(sp => new AddressSettings());
+builder.Services.AddScoped(sp => new DateTimeSettings());
+builder.Services.AddScoped(sp => new CaptchaSettings());
+builder.Services.AddScoped(sp => new DisplayDefaultMenuItemSettings());
+builder.Services.AddScoped(sp => new DisplayDefaultFooterItemSettings());
+builder.Services.AddScoped(sp => new PdfSettings());
+builder.Services.AddScoped(sp => new RobotsTxtSettings());
+builder.Services.AddScoped(sp => new SitemapSettings());
+builder.Services.AddScoped(sp => new SitemapXmlSettings());
+builder.Services.AddScoped(sp => new MeasureSettings());
+builder.Services.AddScoped(sp => new MultiFactorAuthenticationSettings());
+builder.Services.AddScoped(sp => new ProxySettings());
+
+// További Settings-ek (Nop.Core.Domain namespace-ekből)
+builder.Services.AddScoped(sp => new Nop.Core.Domain.Catalog.ProductEditorSettings());
+builder.Services.AddScoped(sp => new Nop.Core.Domain.Messages.MessageTemplatesSettings());
+
+// ===========================================
+// === NOP CORE SZOLGÁLTATÁSOK ===
+// ===========================================
+
+// Web & Utils
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Context
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Plugins
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Settings & Config
+builder.Services.AddScoped();
+
+// Security & Permission
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Authentication
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Store
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Localization
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Currency & Directory
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Customer
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Address & Vendor & Affiliate
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Generic Attribute
+builder.Services.AddScoped();
+
+// Maintenance
+builder.Services.AddScoped();
+
+// Catalog
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Pricing (CustomPriceCalculationService a FruitBank-ból)
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Search
+builder.Services.AddScoped();
+
+// Orders
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Shipping
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Tax
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Payment
+builder.Services.AddScoped();
+
+// Discounts
+builder.Services.AddScoped();
+
+// Media
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Messages & Notifications
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// SEO & HTML
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Logging
+builder.Services.AddScoped();
+
+// Topics & Content
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// GDPR
+builder.Services.AddScoped();
+
+// Export/Import
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Themes
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Schedule Tasks
+builder.Services.AddSingleton();
+builder.Services.AddTransient();
+builder.Services.AddScoped();
+
+// Installation
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Slug Route Transformer (ha van adatbázis)
+builder.Services.AddScoped();
+
+// Routing
+builder.Services.AddSingleton();
+
+// Roxy File Manager
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Web Framework
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Attribute Services (generic)
+builder.Services.AddScoped(typeof(IAttributeService<,>), typeof(AttributeService<,>));
+builder.Services.AddScoped(typeof(IAttributeParser<,>), typeof(Nop.Services.Attributes.AttributeParser<,>));
+builder.Services.AddScoped(typeof(IAttributeFormatter<,>), typeof(AttributeFormatter<,>));
+
+// Plugin Managers (generic)
+builder.Services.AddScoped(typeof(IPluginManager<>), typeof(PluginManager<>));
+
+// ===========================================
+// === PLUGIN MANAGEREK (Null implementációk) ===
+// ===========================================
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// ===========================================
+// === NOP.WEB COMMON FACTORIES (Nop.Web\Infrastructure\NopStartup.cs) ===
+// ===========================================
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// ===========================================
+// === NOP.WEB ADMIN MODEL FACTORIES (Nop.Web\Infrastructure\NopStartup.cs) ===
+// ===========================================
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+// IOrderModelFactory - FruitBank CustomOrderModelFactory felülírja!
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+// IProductModelFactory - FruitBank CustomProductModelFactory felülírja!
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// ===========================================
+// === NOP.WEB PUBLIC FACTORIES (Nop.Web\Infrastructure\NopStartup.cs) ===
+// ===========================================
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// ===========================================
+// === NOP.WEB HELPERS ===
+// ===========================================
+
+builder.Services.AddScoped();
+
+// ===========================================
+// === FRUITBANK PLUGIN SZOLGÁLTATÁSOK (PluginNopStartup-ból) ===
+// ===========================================;
+
+// Logger
+builder.Services.AddScoped();
+builder.Services.AddTransient();
+builder.Services.AddScoped();
+builder.Services.AddSingleton();
+
+// Core
+builder.Services.AddSingleton();
+builder.Services.AddScoped();
+
+// Event Consumer
+builder.Services.AddScoped, EventConsumer>();
+
+// Business Services
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// ReplicateService with HttpClient
+builder.Services.AddScoped();
+builder.Services.AddHttpClient(client =>
+{
+ client.DefaultRequestHeaders.Authorization =
+ new AuthenticationHeaderValue("Bearer", "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh");
+});
+
+// DbTable Services
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// DbContext Services
+builder.Services.AddScoped