diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index fd77ad4b..35b4e8d2 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -85,7 +85,8 @@
"PowerShell($xml = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\" -Recurse -Filter \"DevExpress.Blazor.xml\" -ErrorAction SilentlyContinue | Select-Object -Last 1; if \\($xml\\) { $xml.FullName; Select-String -Path $xml.FullName -Pattern \"MessageBoxOptions\\\\.\\(BodyTemplate|CssClass|Text|Title\\)\" | Select-Object -First 8 | ForEach-Object { $_.Line.Trim\\(\\).Substring\\(0, [Math]::Min\\(120, $_.Line.Trim\\(\\).Length\\)\\) } } else { \"DevExpress.Blazor.xml nem talalhato a NuGet cache-ben\" })",
"PowerShell($lib = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\" -Directory | Sort-Object Name | Select-Object -Last 1; \"Verzio: $\\($lib.Name\\)\"; $dll = Get-ChildItem $lib.FullName -Recurse -Filter \"DevExpress.Blazor.dll\" | Select-Object -First 1; $asm = [System.Reflection.Assembly]::LoadFile\\($dll.FullName\\); $t = $asm.GetType\\(\"DevExpress.Blazor.MessageBoxOptions\"\\); if \\($t\\) { $t.GetProperties\\(\\) | Select-Object -ExpandProperty Name } else { \"MessageBoxOptions tipus nem talalhato\" })",
"PowerShell($dll = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\" -Recurse -Filter \"DevExpress.Blazor.dll\" | Select-Object -First 1; $dll.FullName; $bytes = [System.IO.File]::ReadAllBytes\\($dll.FullName\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"CssClass\",\"MessageBoxOptions\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })",
- "PowerShell($dll = \"C:\\\\Users\\\\Fullepi\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\\\\lib\\\\net8.0\\\\DevExpress.Blazor.v25.1.dll\"; $bytes = [System.IO.File]::ReadAllBytes\\($dll\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"get_CssClass\",\"MessageBoxOptions\",\"TitleTemplate\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })"
+ "PowerShell($dll = \"C:\\\\Users\\\\Fullepi\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\\\\lib\\\\net8.0\\\\DevExpress.Blazor.v25.1.dll\"; $bytes = [System.IO.File]::ReadAllBytes\\($dll\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"get_CssClass\",\"MessageBoxOptions\",\"TitleTemplate\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })",
+ "PowerShell(dotnet test \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Services.Tests\\\\AyCode.Services.Tests.csproj\" --filter \"FullyQualifiedName~EkaerTradeCardValidatorTests\" --nologo -v quiet 2>$null)"
]
}
}
diff --git a/FruitBank.Common.Server/FruitBankConst.cs b/FruitBank.Common.Server/FruitBankConst.cs
index c60e6ca0..ab279d71 100644
--- a/FruitBank.Common.Server/FruitBankConst.cs
+++ b/FruitBank.Common.Server/FruitBankConst.cs
@@ -27,6 +27,30 @@ namespace FruitBank.Common.Server
///
public const string PreOrderWindowEnd = "PreOrderWindowEnd";
+ // ── Environment flag — method-only surface on purpose: no property to bind/serialize/read accidentally ──
+
+ private static bool? _isProd;
+
+ ///
+ /// Set once by the plugin startup after DB-catalog classification.
+ /// A second call with a different value throws — the flag cannot flip at runtime.
+ ///
+ public static void SetIsProd(bool isProd)
+ {
+ if (_isProd.HasValue && _isProd.Value != isProd)
+ throw new InvalidOperationException(
+ $"IsProd is already set to {_isProd.Value}; refusing to change it to {isProd}.");
+
+ _isProd = isProd;
+ }
+
+ ///
+ /// True = PROD database. Throws until SetIsProd ran — an uninitialized read must never return a default.
+ ///
+ public static bool IsProd()
+ => _isProd ?? throw new InvalidOperationException(
+ "IsProd has not been initialized — SetIsProd must run at startup before any read.");
+
static FruitBankConst()
{
ProjectId = Guid.Parse(ProjectIdString);
diff --git a/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs b/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs
index 6ba04041..ef89e8ee 100644
--- a/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs
+++ b/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs
@@ -6,6 +6,9 @@ namespace FruitBank.Common.Server.Services.Loggers;
public class LoggerToLoggerApiController : Logger
{
- public LoggerToLoggerApiController(params IAcLogWriterBase[] logWriters) : base(logWriters)
+ // IEnumerable — a MS DI tömböt ("params IAcLogWriterBase[]") NEM tud feloldani (InvalidOperationException:
+ // Unable to resolve service for type 'IAcLogWriterBase[]'), amitől a LoggerSignalRHub aktiválása minden
+ // kliens-kapcsolódásnál elszállt, és a felküldött kliens-logok némán elvesztek.
+ public LoggerToLoggerApiController(IEnumerable logWriters) : base(logWriters.ToArray())
{ }
}
\ No newline at end of file
diff --git a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs
index 7e1277b4..ffc7e4dc 100644
--- a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs
+++ b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs
@@ -111,6 +111,12 @@ public interface IFruitBankDataControllerCommon
Task LoginMeasuringUser(MgLoginModelRequest loginModelRequest);
Task?> ProcessAndSaveFullShippingJson(string fullShippingJson, int customerId);
+ ///
+ /// True if the server runs against the PROD database. The value is definite by construction
+ /// (an ambiguous DB catalog fails the server startup); consumers must fail closed on any error.
+ ///
+ public Task IsProdEnvironment();
+
public Task?> GetGenericAttributeDtosByEntityIdAndKeyGroup(int productId, string keyGroup, int storeId);
public Task AddGenericAttributeDto(GenericAttributeDto genericAttributeDto);
diff --git a/FruitBank.Common/SignalRs/SignalRTags.cs b/FruitBank.Common/SignalRs/SignalRTags.cs
index d62c2ec3..791cf2ab 100644
--- a/FruitBank.Common/SignalRs/SignalRTags.cs
+++ b/FruitBank.Common/SignalRs/SignalRTags.cs
@@ -131,6 +131,8 @@ public class SignalRTags : AcSignalRTags
public const int AuthenticateUser = 195;
public const int RefreshToken = 200;
+ public const int IsProdEnvironment = 210;
+
#region SendToClient
public const int SendOrderChanged = 500;
public const int SendOrderItemChanged = 501;
diff --git a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs
index a441c500..02bea86d 100644
--- a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs
+++ b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs
@@ -58,6 +58,8 @@ namespace FruitBankHybrid.Shared.Services.SignalRs
public Task?> GetMeasuringModels() => GetAllAsync>(SignalRTags.GetMeasuringModels);
+ public Task IsProdEnvironment() => GetAllAsync(SignalRTags.IsProdEnvironment);
+
#region Partner
public Task?> GetPartners() => GetAllAsync>(SignalRTags.GetPartners);
public Task GetPartnerById(int id) => GetByIdAsync(SignalRTags.GetPartnerById, id);
diff --git a/FruitBankHybrid.Shared/appsettings.json b/FruitBankHybrid.Shared/appsettings.json
index 47f5ae5c..3687fc31 100644
--- a/FruitBankHybrid.Shared/appsettings.json
+++ b/FruitBankHybrid.Shared/appsettings.json
@@ -14,7 +14,7 @@
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
- "AppType": "Server",
+ "AppType": "Web",
"LogLevel": "Detail",
"LogWriters": [
{
diff --git a/FruitBankHybrid.Web.Client/Program.cs b/FruitBankHybrid.Web.Client/Program.cs
index 83cfc671..1cf2d9ba 100644
--- a/FruitBankHybrid.Web.Client/Program.cs
+++ b/FruitBankHybrid.Web.Client/Program.cs
@@ -1,3 +1,4 @@
+using AyCode.Core.Enums;
using AyCode.Core.Loggers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Services.SignalRs;
@@ -43,9 +44,10 @@ builder.Services.Configure(opts =>
});
// Logger options + framework factory. LoggerClient instances are created per caller category,
-// with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase.
+// with LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase.
+// AppType: a WASM kliens kód-defaultja Web — az appsettings (AyCode:Logger:AppType) felülírhatja, ha megadja.
builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger"));
-builder.Services.AddAcLoggerFactory();
+builder.Services.AddAcLoggerFactory(AppType.Web);
// HubConnectionBuilder — transient so each consumer gets a fresh builder to Build().
// All connection and protocol configuration flows from appsettings.json via IOptions;
diff --git a/FruitBankHybrid.Web.Client/wwwroot/appsettings.json b/FruitBankHybrid.Web.Client/wwwroot/appsettings.json
index 47f5ae5c..3687fc31 100644
--- a/FruitBankHybrid.Web.Client/wwwroot/appsettings.json
+++ b/FruitBankHybrid.Web.Client/wwwroot/appsettings.json
@@ -14,7 +14,7 @@
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
- "AppType": "Server",
+ "AppType": "Web",
"LogLevel": "Detail",
"LogWriters": [
{
diff --git a/FruitBankHybrid.Web/Program.cs b/FruitBankHybrid.Web/Program.cs
index 56965a95..05d85dff 100644
--- a/FruitBankHybrid.Web/Program.cs
+++ b/FruitBankHybrid.Web/Program.cs
@@ -1,3 +1,4 @@
+using AyCode.Core.Enums;
using AyCode.Core.Loggers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Services.SignalRs;
@@ -26,9 +27,10 @@ builder.Services.AddSingleton();
// Logger options + framework factory. LoggerClient instances are created per caller category,
-// with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterBase.
+// with LogLevel from appsettings and writers resolved from DI via IAcLogWriterBase.
+// AppType: a hybrid WEB-host kód-defaultja Web (az [S] a nop-szerveré) — az appsettings felülírhatja, ha megadja.
builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger"));
-builder.Services.AddAcLoggerFactory();
+builder.Services.AddAcLoggerFactory(AppType.Web);
builder.Services.AddSingleton(sp => new LoggedInModel(sp.GetRequiredService()));
diff --git a/FruitBankHybrid.Web/appsettings.json b/FruitBankHybrid.Web/appsettings.json
index 47f5ae5c..3687fc31 100644
--- a/FruitBankHybrid.Web/appsettings.json
+++ b/FruitBankHybrid.Web/appsettings.json
@@ -14,7 +14,7 @@
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
- "AppType": "Server",
+ "AppType": "Web",
"LogLevel": "Detail",
"LogWriters": [
{
diff --git a/FruitBankHybrid/MauiProgram.cs b/FruitBankHybrid/MauiProgram.cs
index f310f25c..24f7c1f0 100644
--- a/FruitBankHybrid/MauiProgram.cs
+++ b/FruitBankHybrid/MauiProgram.cs
@@ -1,4 +1,5 @@
-using AyCode.Core.Loggers;
+using AyCode.Core.Enums;
+using AyCode.Core.Loggers;
using AyCode.Services.SignalRs;
using FruitBank.Common.Loggers;
using FruitBank.Common.Models;
@@ -51,9 +52,10 @@ namespace FruitBankHybrid
builder.Services.AddSingleton();
// Logger options + framework factory. LoggerClient instances are created per caller category,
- // with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase.
+ // with LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase.
+ // AppType: a MAUI kliens kód-defaultja Web — az appsettings felülírhatja (pl. Mobile-ra), ha megadja.
builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger"));
- builder.Services.AddAcLoggerFactory();
+ builder.Services.AddAcLoggerFactory(AppType.Web);
// Bind SignalR options from configuration.
// Precedence: code default → appsettings.json (this line) → any later Configure action.