Compare commits
2 Commits
dbc7fd31d3
...
f70cb237d8
| Author | SHA1 | Date |
|---|---|---|
|
|
f70cb237d8 | |
|
|
4a16943d3e |
|
|
@ -100,7 +100,15 @@
|
|||
"PowerShell(\\(Get-Content \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Blazor\\\\AyCode.Blazor.Components\\\\docs\\\\MGGRID\\\\MGGRID_TODO.md\" | Measure-Object -Line\\).Lines)",
|
||||
"PowerShell(\"=== FruitBank.Common ===\")",
|
||||
"Bash(git check-ignore *)",
|
||||
"PowerShell(dotnet build \"H:\\\\Applications\\\\Mango\\\\Source\\\\FruitBankHybridApp\\\\FruitBank.Common.Server\\\\FruitBank.Common.Server.csproj\" -v q --nologo 2>&1 | Select-String -Pattern \"error CS\" | Select-Object -First 20; \"--- CS hibak szama: ---\"; \\(dotnet build \"H:\\\\Applications\\\\Mango\\\\Source\\\\FruitBankHybridApp\\\\FruitBank.Common.Server\\\\FruitBank.Common.Server.csproj\" -v q --nologo 2>&1 | Select-String -Pattern \"error CS\" | Measure-Object\\).Count)"
|
||||
"PowerShell(dotnet build \"H:\\\\Applications\\\\Mango\\\\Source\\\\FruitBankHybridApp\\\\FruitBank.Common.Server\\\\FruitBank.Common.Server.csproj\" -v q --nologo 2>&1 | Select-String -Pattern \"error CS\" | Select-Object -First 20; \"--- CS hibak szama: ---\"; \\(dotnet build \"H:\\\\Applications\\\\Mango\\\\Source\\\\FruitBankHybridApp\\\\FruitBank.Common.Server\\\\FruitBank.Common.Server.csproj\" -v q --nologo 2>&1 | Select-String -Pattern \"error CS\" | Measure-Object\\).Count)",
|
||||
"PowerShell(git -C \"H:\\\\Applications\\\\Mango\\\\Source\\\\FruitBankHybridApp\" show --stat --name-only dbc7fd3)",
|
||||
"Read(//h/Applications/Aycode/Source/AyCode.Core/AyCode.Services/docs/**)",
|
||||
"Read(//h/Applications/Aycode/Source/AyCode.Core/AyCode.Services/docs/SIGNALR_DATASOURCE/**)",
|
||||
"Read(//h/Applications/Aycode/Source/AyCode.Core/AyCode.Services.Server/docs/**)",
|
||||
"Read(//h/Applications/Aycode/Source/AyCode.Core/AyCode.Services/SignalRs/**)",
|
||||
"Read(//h/Applications/Aycode/Source/AyCode.Core/AyCode.Services.Server/SignalRs/**)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"WebFetch(domain:docs.devexpress.com)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ namespace FruitBank.Common.Server
|
|||
/// </summary>
|
||||
public const string PreOrderWindowEnd = "PreOrderWindowEnd";
|
||||
|
||||
// ── Environment flag — method-only surface on purpose: no property to bind/serialize/read accidentally ──
|
||||
|
||||
private static bool? _isProd;
|
||||
// ── Environment flag → AcEnv.IsProdDb (AyCode.Core framework, 2026-07) ──
|
||||
// Egy-forrás keret-állapot (ugyanaz, amit a kliens tükröz). A method-only felület szándékosan megmarad:
|
||||
// nincs property, amit véletlenül bind-olni/szerializálni lehetne. A set-once őr (a szerver DB-je nem
|
||||
// változhat futásidőben) itt marad — az AcEnv-írás előtt.
|
||||
|
||||
/// <summary>
|
||||
/// Set once by the plugin startup after DB-catalog classification.
|
||||
|
|
@ -37,19 +38,17 @@ namespace FruitBank.Common.Server
|
|||
/// </summary>
|
||||
public static void SetIsProd(bool isProd)
|
||||
{
|
||||
if (_isProd.HasValue && _isProd.Value != isProd)
|
||||
if (AcEnv.IsProdDbHydrated && AcEnv.IsProdDb != isProd)
|
||||
throw new InvalidOperationException(
|
||||
$"IsProd is already set to {_isProd.Value}; refusing to change it to {isProd}.");
|
||||
$"IsProd is already set to {AcEnv.IsProdDb}; refusing to change it to {isProd}.");
|
||||
|
||||
_isProd = isProd;
|
||||
AcEnv.SetIsProdDb(isProd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True = PROD database. Throws until SetIsProd ran — an uninitialized read must never return a default.
|
||||
/// </summary>
|
||||
public static bool IsProd()
|
||||
=> _isProd ?? throw new InvalidOperationException(
|
||||
"IsProd has not been initialized — SetIsProd must run at startup before any read.");
|
||||
public static bool IsProd() => AcEnv.IsProdDb;
|
||||
|
||||
static FruitBankConst()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,30 +17,19 @@ public static class FruitBankConstClient
|
|||
set => AcEnv.BaseUrl = value;
|
||||
}
|
||||
|
||||
// A szerver-környezet (PROD/DEV) — az érték KIZÁRÓLAG a szerver IsProdEnvironment() válaszából jöhet
|
||||
// (a szerver a DB-katalógusból sorolja be magát, fail-closed módon), URL-alakból/configból SOHA.
|
||||
// Az EGYETLEN értékadási pont: FruitBankSignalRClient (kapcsolat-felépüléskor hidratál, ld. EnsureEnvironmentAsync).
|
||||
// Hidratálatlan olvasás → exception (fail fast); sikertelen lekérdezés → marad hidratálatlan (fail closed).
|
||||
private static bool? _isProd;
|
||||
public static bool IsProd
|
||||
=> _isProd ?? throw new InvalidOperationException(
|
||||
"FruitBankConstClient.IsProd nincs hidratálva — a FruitBankSignalRClient a kapcsolat felépülésekor tölti fel a szerver IsProdEnvironment() válaszából (await client.EnsureEnvironmentAsync()).");
|
||||
// IsProd → AcEnv.IsProdDb (AyCode.Core framework, 2026-07): a szerver ADAT-környezete (a mögöttes DB PROD-e),
|
||||
// NEM build-konfiguráció. Delegát az egy-forrás keret-állapotra (fail-fast + változás-esemény) — a hívóhelyek
|
||||
// változatlanok. Az érték forrása: a FruitBankSignalRClient a kapcsolat felépülésekor hidratál (EnsureEnvironmentAsync).
|
||||
public static bool IsProd => AcEnv.IsProdDb;
|
||||
public static bool IsProdHydrated => AcEnv.IsProdDbHydrated;
|
||||
public static void SetIsProd(bool isProd) => AcEnv.SetIsProdDb(isProd);
|
||||
|
||||
/// <summary>Az IsProd TÉNYLEGES változásakor tüzel (első hidratálás + announce-detektált env-váltás) —
|
||||
/// a megjelenítők (title-badge, UI-sáv) feliratkozási pontja. Az értékadás forrása változatlanul EGY hely.</summary>
|
||||
public static event Action<bool>? IsProdChanged;
|
||||
|
||||
public static void SetIsProd(bool isProd)
|
||||
public static event Action<bool>? IsProdChanged
|
||||
{
|
||||
var changed = _isProd != isProd;
|
||||
_isProd = isProd;
|
||||
|
||||
if (changed) IsProdChanged?.Invoke(isProd);
|
||||
add => AcEnv.IsProdDbChanged += value;
|
||||
remove => AcEnv.IsProdDbChanged -= value;
|
||||
}
|
||||
|
||||
/// <summary>True, ha az IsProd már hidratált — kapu-logikához (első-connect await) és a push-összevetéshez.</summary>
|
||||
public static bool IsProdHydrated => _isProd != null;
|
||||
|
||||
public static string DefaultHubName = "fbHub";
|
||||
public static string LoggerHubName = "loggerHub";
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using AyCode.Core.Extensions;
|
|||
using AyCode.Core.Loggers;
|
||||
using AyCode.Utils.Extensions;
|
||||
using DevExpress.Blazor;
|
||||
using DevExpress.Data.Filtering;
|
||||
using DevExpress.Data.Helpers;
|
||||
using DevExpress.Utils.Filtering.Internal;
|
||||
using FruitBank.Common.Models;
|
||||
|
|
@ -57,6 +58,10 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
// Not implemented in this legacy class - will be removed
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetFieldQuickFilter(string fieldName, CriteriaOperator? criteria)
|
||||
=> SetFieldFilterCriteria(fieldName, criteria); // legacy class - will be removed
|
||||
|
||||
[Inject] public required IEnumerable<IAcLogWriterClientBase> LogWriters { get; set; }
|
||||
[Inject] public required FruitBankSignalRClient FruitBankSignalRClient { get; set; }
|
||||
[Inject] public required LoggedInModel LoggedInModel { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
# ADR 0002: Környezet-infrastruktúra (BaseUrl, szerver-adatkörnyezet, env-badge) keretbe emelése
|
||||
|
||||
## Status
|
||||
|
||||
Accepted (2026-07-15) — végrehajtás lépésenként, per-lépés review-val; 1. lépés (BaseUrl → AcEnv) kész.
|
||||
|
||||
## Context
|
||||
|
||||
- **Kiváltó incidens:** a fbHub és a loggerHub URL-je két külön forrásból élt (`AcHubConnection:Url` appsettings vs. `FruitBankConstClient.BaseUrl` kód-konstans) → a kliens a helyi nop helyett a PROD `shop.fruitbank.hu`-ra töltötte fel a logokat, órákig félrevitt nyomozással. A gyökér: az URL-infrastruktúra a fogyasztói const-osztályon, több igazságforrással.
|
||||
- **Frissen épült env-lánc (2026-07):** szerver-adatkörnyezet (PROD/DEV) kliens-oldali ismerete — first-connect kapu (await, fail-closed), szerver push-on-connect announce (env-váltás riasztással), `[PROD]/[DEV]` title-badge, MSTest prod-guard. Élesben igazolva (DEV→PROD DB-váltás restarttal: announce + riasztás + title-váltás működött). **Mindez FruitBank-oldalon született, pedig app-agnosztikus.**
|
||||
- **Redundancia:** ugyanaz a fogalom kétszer — szerver `FruitBankConst.SetIsProd` + kliens `FruitBankConstClient.IsProd`; a teszt-guard, badge, jövőbeli destruktív-védelem mind külön-külön kötné be.
|
||||
- **„IsProd" tengely-probléma:** három független „prod"-tengely létezik — (1) build-konfiguráció (Debug/Release), (2) adat-környezet (melyik DB-katalógus), (3) hosting env (ASPNETCORE_ENVIRONMENT). A Debug-build + PROD-DB + Development-hosting kombináció élő teszttel bizonyított — egyetlen `IsProd` név ezeket összemosná.
|
||||
- **Időzítés:** a közös nagyker-réteg terv (VeggieVoyage) előtt; a ház-mintában van közvetlen precedens: **GridLayout-roaming** (framework-fenntartott tagek `AcSignalRTags` 9001x + framework-kliens-logika + fogyasztói endpoint-implementáció).
|
||||
- **Scope IN:** AcEnv állapotok (BaseUrl, IsProdDb); framework-tagek; kliens-base env-gépezet; hub-base announce; Blazor title-réteg; FruitBank-vékonyítás delegátokkal. **Scope OUT:** hosting-env tengely (később, read-only diagnosztika); `IsProdBuild` (független bónusz); `AcEnv.SetConfiguration` config-egységesítés (ACCORE-LOG-T-N2D8 — szinergia, nem feltétel).
|
||||
|
||||
## Decision
|
||||
|
||||
**A környezet-infrastruktúra a keretbe kerül** — a fogyasztó csak opt-inel, endpointot implementál és domain-konstansokat tart:
|
||||
|
||||
1. **Egy-forrás állapotok az `AcEnv`-en** (AyCode.Core): `BaseUrl` ✅ (kész) és `IsProdDb` (+`Set`/`Hydrated`/`Changed` esemény) — a szervernél a DB-katalógus-besorolás tölti (fail-closed), a kliens tükrözi (kapu + announce). A megosztás legitim: a kliensnek nincs saját DB-je, az ő adatkörnyezete a szerveré.
|
||||
2. **Tengelyenkénti nevezés:** `IsProdDb` (adat-tengely, wire-en szinkronizált) ≠ `IsProdBuild` (build-tengely, processenkénti, SOHA nem megy wire-ra) ≠ hosting env. Nincs csupasz `IsProd` a kereten.
|
||||
3. **Framework-fenntartott SignalR-tagek** (`AcSignalRTags`, a GridLayout 9001x mintájára): query 90020, announce 90021 — a fogyasztói 210/211 kivezetve (wire-váltás: kliens+szerver együtt-deploy).
|
||||
4. **Kliens-gépezet az `AcSignalRClientBase`-be:** first-connect kapu (await + dedup: pontosan 1 wire-hívás), `EnsureEnvironmentAsync`, fail-closed nyers query (timeout ≠ `default(bool)`!), announce-elfogás a dispatch-ben (a fogyasztói `MessageReceived` nem is látja). **Opt-in**: `EnvironmentFeatureEnabled => false` default — más fogyasztók (pl. TourIAm) szervere nem ismeri az endpointot, default-on fail-closed törést okozna.
|
||||
5. **Hub-base announce:** push-on-connect az `AcEnv.IsProdDb`-ből (best-effort, a kapcsolatot nem dönti).
|
||||
6. **Blazor title-réteg** (AyCode.Blazor.Components): `IAcAppTitleService` + browser-implementáció + `<AcEnvironmentTitleBadge AppName="..."/>`; MAUI-implementáció fogyasztói marad (a keretnek nincs MAUI-assemblyje).
|
||||
7. **Delegát-minta a fogyasztón:** a `FruitBankConstClient` érintett tagjai átlátszó delegátként maradnak (`BaseUrl` ✅) — nulla törés, a hívóhelyek változatlanok, a felfedezhetőség megmarad (static class nem örökölhet → tag-migráció + delegát, nem literál öröklés).
|
||||
8. **Fogyasztónál marad:** domain-konstansok (táblanevek, DomainDescription, email-sablonok), a katalógus-besorolás hívási helye, a query-endpoint implementáció (framework-taggel, GridLayout-minta), MAUI title-impl, AppName.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive:**
|
||||
- Minden jövőbeli app (VeggieVoyage, közös nagyker-réteg) **ingyen** kapja a teljes env-láncot: kapu, announce+riasztás, badge, teszt-guard.
|
||||
- A szerver/kliens redundancia megszűnik (egy állapot, egy esemény, sok olvasó); a BaseUrl-divergencia osztálya strukturálisan kizárt.
|
||||
- Egységes fail-fast (hidratálatlan olvasás dob) + fail-closed (nem igazolható env = tiltás) szemantika keret-szinten.
|
||||
|
||||
**Negative:**
|
||||
- Wire-tag átszámozás (210/211 → 9002x): kliens + szerver **együtt-deploy** kötelező a váltáskor.
|
||||
- `AcEnv` statikus állapot: process-szintű — párhuzamos teszt-kollekciók osztoznak rajta (a jelen MSTest-felállásban nem gond, de tudni kell róla).
|
||||
- Az opt-in flag elfelejtése = a feature **némán off** — a keret-doksiban kiemelten dokumentálandó.
|
||||
- A delegát-réteg fegyelmet kér: új infra-tag a KERETRE kerüljön, ne a fogyasztói const-ra.
|
||||
|
||||
**Follow-ups required:**
|
||||
- 2. lépés: `IsProdDb` → `AcEnv` (FruitBank-oldali `IsProd`-tagok delegátként).
|
||||
- 3. lépés: `AcSignalRTags` 90020/90021 + kliens-base gépezet + opt-in flag + teszt-guard helper a keretbe.
|
||||
- 4. lépés: hub-base announce.
|
||||
- 5. lépés: Blazor title-réteg + FruitBank-átkötés (MainLayout → `<AcEnvironmentTitleBadge>`).
|
||||
- Bónusz/később: `IsProdBuild` (+ opcionális build↔DB keresztellenőrzés, opt-in); `AcEnv.SetConfiguration` (N2D8); SignalR-intervallum konstansok configba (a `SignarlR` typóval együtt kivezetve); MAUI-Android `10.0.2.2` BaseUrl-kérdés.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Fogyasztói szinten hagyni** (rejected): minden app újraimplementálná; a redundancia már most élő bugot szült (BaseUrl-divergencia).
|
||||
- **Teljes átnevezés homlokzat nélkül** (a fogyasztói kód mindenhol `AcEnv.*`-ot hív) (rejected): ~12 hívóhely churn, és a csapat a megszokott helyen (`FruitBankConstClient`) keresi az értéket — a delegát ugyanazt az egy-forrást adja nulla töréssel.
|
||||
- *Reversibility:* a delegát később bármikor kivezethető, ha a csapat átszokott.
|
||||
- **Egyetlen `AcEnv.IsProd` név tengely-bontás nélkül** (rejected): a build/adat/hosting tengelyek összemosása — a Debug-build + PROD-DB élő eset bizonyítja, hogy függetlenek; a névnek a forrást kell mondania (`IsProdDb`).
|
||||
- **Reconnect-enkénti újra-lekérdezés a push-on-connect helyett** (rejected mint végállapot): működik, de ellentmond a „csak az elején 1x" elvnek; a push-on-CHANGE pedig konstrukciósan halott (az env-váltás csak restarttal jöhet, ami bontja a csatornát, amin a push utazna) — ezért query-once + announce-on-connect a végleges alak.
|
||||
- **Külön köztes osztály (`AcEnvironmentAwareSignalRClientBase`)** (rejected): felesleges hierarchia-szint; az opt-in flag az `AcSignalRClientBase`-en ugyanazt adja ceremónia nélkül.
|
||||
|
||||
## Related
|
||||
|
||||
- Related ADRs: —
|
||||
- Related TODOs/Issues: `ACCORE-LOG-T-N2D8` (AcEnv.SetConfiguration — szinergia), `ACCORE-LOG-T-D7S3` (protokoll-diagnosztikai logger, átgondolandó), `ACCORE-SBP-T-T5R2` (protokoll-trace Trace-szintre), `ACBLAZOR-GRID-T-E3B6` (template-ErrorBoundary).
|
||||
- Precedens: GridLayout-roaming (framework-tag `AcSignalRTags` 9001x + fogyasztói endpoint) — `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md` „Grid Layout Roaming".
|
||||
- External references: —
|
||||
|
|
@ -9,3 +9,4 @@ Architektúra-szintű döntések durable rekordjai (Nygard-stílus): egy fájl =
|
|||
| ADR | Cím | Status |
|
||||
|---|---|---|
|
||||
| [0001](0001-non-measurable-item-derived-weight.md) | Nem mérendő tétel súlya — derivált pallet-mérés a termék átlagsúlyából és tárájából | Proposed (2026-07-14) |
|
||||
| [0002](0002-environment-infrastructure-to-framework.md) | Környezet-infrastruktúra (BaseUrl, szerver-adatkörnyezet, env-badge) keretbe emelése | Accepted (2026-07-15) |
|
||||
|
|
|
|||
Loading…
Reference in New Issue