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 }