verify_totp fetches a CSRF token from the bambulab.com web origin before
posting the code (#2696) and returns early when it cannot get one. These
tests patch only post, so the pre-flight GET went out for real: it succeeded
wherever bambulab.com was reachable and returned a tokenless 403 on a CI
runner, where six tests then asserted on a post that never happened.
Stub the handshake for the module. It is covered end to end, no-token path
included, in tests/unit/test_cloud_totp_csrf.py.
get_setting_detail and delete_setting were hitting
/v1/iot-service/api/slicer/setting/{id} without the version query
parameter Bambu Cloud requires — every call returned HTTP 400
"field 'version' is not set". The sibling plural GET
(get_slicer_settings) has always sent it; the comment above
_SLICER_API_VERSION documents the contract for the endpoint subtree.
Missed when the placeholder landed in the 2026-05-12 compliance rework.
Downstream effect: slicer_filament_resolver.resolve_slicer_filament's
PFUS branch swallowed the 400, fell through to normalize_slicer_filament,
and caller inventory.py generic-material-fell-back tray_info_idx to
GFL99/GFG99. BambuStudio's AMS panel reads the printer's tray_info_idx
echo, so the user saw "Generic PLA" instead of the custom cloud preset.
Masked for 50 days by two rescue paths in the caller: prior-slot
tray_info_idx reuse, and stored spool_k_profile → live state.kprofiles
realign. Reporter's spool 54 → tray 2 assign had neither.
Adjacent surfaces also fixed by the same two-line change: the delete
cloud preset UI route, the whole update_setting flow (get_setting_detail
→ delete_setting → POST), preset_resolver's cloud branch, and three
UI-facing cloud.py routes that fetch setting detail.
get_setting_detail also includes the truncated response body in the
raised BambuCloudError so the next contract change is self-diagnostic
from support-bundle logs.
When Cloudflare in front of bambulab.com returns a "Just a moment..." interstitial
instead of the JSON the API normally produces, the parse error in
verify_totp / verify_code / login_request used to surface as the opaque "Invalid
response from Bambu Cloud" or a generic 401 from BambuCloudAuthError. Reporter
hit this with three back-to-back TOTP attempts; a curl from a different network
with the same honest Bambuddy UA returns clean JSON, so the trigger is CF-side
(per-IP / TLS-fingerprint / rate / transient mitigation), not our code.
Add a small _detect_cloudflare_challenge() helper that inspects the response for
four CF markers (body "Just a moment...", body "challenges.cloudflare.com", 403
with cf-mitigated header, 503 with cf-ray header) and returns a message that
attributes the block to Bambu Lab's Cloudflare protection, suggests waiting a few
minutes, and points the user at a same-network browser sign-in as the standard
workaround. Wired into all three JSON-parse sites; verify_totp previously had a
defensive catch, login_request and verify_code now do too.
No header changes, no impersonation, no retry loop - pure diagnostics. Stays
clearly on the right side of Bambu Lab's "no falsified client identity" line.
feat(cloud): support China region for token-based login
The /cloud/token endpoint always used the global Bambu API endpoint,
so users with China-region access tokens could not validate their
token. The password login flow already exposes a region selector; this
brings the token flow to parity.
TOTP (Two-Factor Authentication):
- Detect TOTP vs email verification from Bambu API loginType response
- Use dedicated TFA endpoint on bambulab.com (not api.bambulab.com)
- Include browser-like headers to bypass Cloudflare protection
- Extract token from JSON response or cookies
- Frontend shows appropriate messages for each verification type
- Added i18n translations for TOTP UI (en, de, ja)
Closes#182