feat(status): app-wide server-unreachable banner with recovery poll (MAPPS-333) #383

Merged
vas2000-work merged 2 commits from feat/MAPPS-333-server-unreachable-banner into main 2026-06-30 05:30:14 +02:00
Owner

What

When mokosh-server is unreachable the app surfaced a scatter of cryptic per-page toasts, empty pages, or CORS-looking console errors, with no single clear signal that the backend was down. This reflects it as one app-wide state instead, and clears it automatically on recovery. Closes MAPPS-333.

Changes

  • Add a SERVER_REACHABLE GlobalSignal (default reachable) in hooks::fetch, classified by the central request helpers: a transport/connection failure, timeout, or 5xx flips it to down; any real response (incl. 4xx) clears it. The opaque "Failed to fetch" / CORS-looking rejection is classified here as server-unreachable and is never shown to the user as a CORS message. 4xx (auth, validation) is not "down" and keeps surfacing through the normal per-call paths.
  • ServerStatusBanner mounted at AppLayout: a single non-blocking "Server unreachable. Reconnecting..." bar while down, with a bold + underlined "Click to see more..." link to the System Status page (per the in-thread request). Pages stay visible; existing per-page toasts are unchanged. Renders nothing while healthy (zero-height, and keeps the link out of the tab order until actionable).
  • use_server_status_monitor at the App root: polls /ready every 10s only while down and stops on first success (the probe's own classification flips the flag back), so there is no added traffic while healthy and the banner auto-dismisses on recovery with no user action. Implemented as a use_resource subscribed to the flag: idle while reachable, looping while down.

Notes

  • GlobalSignal (not a context Signal) for the same reason TENANT_GENERATION is one: the api helpers are plain async fns, not components, and must flip the flag from outside the reactive tree. WASM is single-threaded, so a GlobalSignal is the right primitive (same rationale as the toast surface).
  • Mokosh App sets no CSP of its own, so the "distinguish a CSP block from a genuine outage" acceptance criterion is not applicable here; the classifier and banner only ever speak in user-facing "server unreachable" terms. (The related CSP false-positive class is handled server-side in BUNYIP-244.)

Verification

All green in the rust-builder-glibc:v1.0.1-rust1.94-trixie CI container (and the pre-commit hook): cargo fmt --all --check, cargo clippy --all-targets -- -D warnings, cargo check --target wasm32-unknown-unknown, cargo test --lib (189 tests).

Manual verification (live stack, not exercisable here): stopping mokosh-server shows the banner within one poll interval with a console free of unexplained CORS errors; restarting it clears the banner automatically.

## What When mokosh-server is unreachable the app surfaced a scatter of cryptic per-page toasts, empty pages, or CORS-looking console errors, with no single clear signal that the backend was down. This reflects it as one app-wide state instead, and clears it automatically on recovery. Closes MAPPS-333. ## Changes - Add a `SERVER_REACHABLE` GlobalSignal (default reachable) in `hooks::fetch`, classified by the central request helpers: a transport/connection failure, timeout, or `5xx` flips it to down; any real response (incl. `4xx`) clears it. The opaque "Failed to fetch" / CORS-looking rejection is classified here as server-unreachable and is never shown to the user as a CORS message. `4xx` (auth, validation) is not "down" and keeps surfacing through the normal per-call paths. - `ServerStatusBanner` mounted at `AppLayout`: a single non-blocking "Server unreachable. Reconnecting..." bar while down, with a bold + underlined "Click to see more..." link to the System Status page (per the in-thread request). Pages stay visible; existing per-page toasts are unchanged. Renders nothing while healthy (zero-height, and keeps the link out of the tab order until actionable). - `use_server_status_monitor` at the App root: polls `/ready` every 10s only while down and stops on first success (the probe's own classification flips the flag back), so there is no added traffic while healthy and the banner auto-dismisses on recovery with no user action. Implemented as a `use_resource` subscribed to the flag: idle while reachable, looping while down. ## Notes - GlobalSignal (not a context `Signal`) for the same reason `TENANT_GENERATION` is one: the `api` helpers are plain async fns, not components, and must flip the flag from outside the reactive tree. WASM is single-threaded, so a GlobalSignal is the right primitive (same rationale as the toast surface). - Mokosh App sets no CSP of its own, so the "distinguish a CSP block from a genuine outage" acceptance criterion is not applicable here; the classifier and banner only ever speak in user-facing "server unreachable" terms. (The related CSP false-positive class is handled server-side in BUNYIP-244.) ## Verification All green in the `rust-builder-glibc:v1.0.1-rust1.94-trixie` CI container (and the pre-commit hook): `cargo fmt --all --check`, `cargo clippy --all-targets -- -D warnings`, `cargo check --target wasm32-unknown-unknown`, `cargo test --lib` (189 tests). Manual verification (live stack, not exercisable here): stopping mokosh-server shows the banner within one poll interval with a console free of unexplained CORS errors; restarting it clears the banner automatically.
feat(status): app-wide server-unreachable banner with recovery poll
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 13m26s
f1621607b5
When mokosh-server is unreachable the app surfaced a scatter of cryptic per-page toasts, empty pages, or CORS-looking console errors, with no single clear signal that the backend was down. Reflect it instead as one app-wide state.

Add a SERVER_REACHABLE GlobalSignal (default reachable) that the central fetch helpers classify into: a transport/connection failure, timeout, or 5xx flips it to down (the opaque "Failed to fetch" / CORS-looking rejection is classified here as server-unreachable, never shown to the user as a CORS message); any other response (incl. 4xx) clears it, so auth/validation errors keep surfacing as they do today. A ServerStatusBanner mounted at AppLayout renders a single non-blocking "Server unreachable. Reconnecting..." bar while down, with a bold + underlined "Click to see more..." link to the System Status page; pages stay visible and per-page toasts are unchanged. A use_server_status_monitor hook at the App root polls /ready every 10s only while down and stops on first success (the probe's own classification flips the flag back), so there is no added traffic while healthy and the banner auto-dismisses on recovery with no user action.

Mokosh App sets no CSP of its own, so there is no CSP-block case to distinguish from a genuine outage here; the classifier and banner only ever speak in user-facing "server unreachable" terms.

Verified in the rust-builder-glibc CI container: cargo fmt --all --check, cargo clippy --all-targets -- -D warnings, cargo check --target wasm32-unknown-unknown, and cargo test --lib (189 tests) all green.

#MAPPS-333
Merge branch 'main' into feat/MAPPS-333-server-unreachable-banner
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 1m23s
Create release / Create release from merged PR (pull_request) Has been skipped
4599b20cf4
vas2000-work scheduled this pull request to auto merge when all checks succeed 2026-06-30 05:28:59 +02:00
vas2000-work deleted branch feat/MAPPS-333-server-unreachable-banner 2026-06-30 05:30:14 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!383
No description provided.