From 93a46d4de70f12b367b7cd146897aef102b7ed6d Mon Sep 17 00:00:00 2001 From: OpenFUT Agent Date: Thu, 13 Aug 2026 22:39:36 +0000 Subject: [PATCH] feat(fifa17): cut over FUT economy authority to Rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The economy authority barrier. `handle_with_ip` now dispatches every economy-touching route to Rust/Core via `try_handle_economy` BEFORE consulting `classify()`, so a migrated route can never also reach the Python passthrough (NEVER BOTH). With economy services wired (production `from_config`) an economy route ALWAYS returns Some — fail-closed 503 on any Core error — so there is no Python economy fallback. `userMassInfo` stays a hybrid by design: Python supplies the non-economy envelope; Rust overlays BOTH the squad and the economy fields (coins + unopened-pack count from Core), so no stale Python economy value is visible. Routing only — no handler/test changes buried here. Routes now Rust-owned: /user/credits, /store/purchasegroup, /store/transaction, POST+GET /purchased, PUT /item, item DELETE forms, /match (ut/delete), /auctionhouse, /tradePile, /trade, ut/delete trade; plus the userMassInfo economy overlay. --- openfut-utas-host/src/lib.rs | 41 +++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/openfut-utas-host/src/lib.rs b/openfut-utas-host/src/lib.rs index 2dbb317..7b1e705 100644 --- a/openfut-utas-host/src/lib.rs +++ b/openfut-utas-host/src/lib.rs @@ -2084,6 +2084,21 @@ impl Server { client_ip: Option<&str>, ) -> WireResponse { let path = target.split('?').next().unwrap_or(target); + // ─────────────────────── Economy authority barrier ─────────────────── + // Every economy-touching route is owned by Rust/Core. Classified and + // dispatched HERE, before `classify()`, so a migrated route can NEVER also + // reach the Python passthrough (NEVER BOTH). When the economy services are + // wired (production `from_config`), an economy route ALWAYS returns `Some` + // — fail-closed (503) on any Core error — so there is no Python economy + // fallback. `None` means "not an economy route" (or no economy wired, i.e. + // a bare test server), which falls through to the classifier below. + if let Some(resp) = self.try_handle_economy(method, target, headers, body, client_ip) { + eprintln!( + "utas-host owner=RUST route=economy method={} path={} status={}", + method, path, resp.status + ); + return resp; + } match classify(method, path) { Route::Club => { let query = target.split_once('?').map(|(_, q)| q).unwrap_or(""); @@ -2128,11 +2143,31 @@ impl Server { } Route::UserMassInfo => { let deps = self.squad_deps(); - let (resp, log) = + let (mut resp, log) = handle_user_mass_info(method, target, headers, body, &deps, self.pass.as_ref()); + // Overlay the authoritative Core economy (coins + unopened-pack + // count) so NO stale Python economy value is visible post-barrier. + // userMassInfo remains a hybrid by design: Python supplies the + // non-economy envelope; Rust owns the squad AND the economy fields. + let mut econ_overlaid = false; + if let Some(svc) = &self.economy { + if (200..300).contains(&resp.status) { + if let (Ok(coins), Ok(ents)) = (svc.econ.balance(), svc.econ.entitlements()) + { + if let Ok(mut root) = serde_json::from_slice::(&resp.body) { + if overlay_massinfo_economy(&mut root, coins, ents.len()) { + if let Ok(nb) = serde_json::to_vec(&root) { + set_json_body(&mut resp, nb); + econ_overlaid = true; + } + } + } + } + } + } eprintln!( - "utas-host owner=RUST_OVERLAY route=userMassInfo status={} squad_outcome={} detail=[{}]", - resp.status, log.outcome, log.detail + "utas-host owner=RUST_OVERLAY route=userMassInfo status={} squad_outcome={} econ_overlaid={} detail=[{}]", + resp.status, log.outcome, econ_overlaid, log.detail ); resp }