fix(oracle-r2): same-origin baseUrl + refresh actually re-fetches
Oracle round-2 review caught two real bugs: 1. Production baseUrl bypassed the nginx /api proxy - api.ts defaulted to https://demo.flow-master.ai in prod - Browser would hit cross-origin and CORS-fail - Now: baseUrl='' everywhere; nginx.conf already reverse-proxies /api/* - vite.config.ts proxy still handles dev 2. Refresh button didn't refresh - setMode('live') early-returned when already in live mode - Now: setMode() and refreshLive() share runLiveFetch(); refreshLive ignores the same-mode guard and always re-runs - 4 new vitest regressions in state/store.test.ts cover the contract - Smoke now asserts /api/ea2/work-items is called twice after Refresh Also: - buildScenarios.ts parallelized: cap N=6 candidates, Promise.all per- candidate fetches → live mode now ~3s instead of 30s - CommandBar + LeftRail preview toasts now name the exact endpoint (/api/runtime/transactions/{id}/actions) in the visible text - Landing 'Go live' button rebound to refreshLive() when already live; copy changed to 'Live · refresh' - README: scenario table now renders (added separator row); deploy section points at the real ops PR + the actual overlay path (overlays/demo, not overlays/mc.flow-master.ai); CORS doc clarifies same-origin requirement Constraint: browsers reject cross-origin → same-origin /api/* required Rejected: dev/prod baseUrl divergence | created production bug Confidence: high Scope-risk: narrow Not-tested: production image actually built + served by ops PR (gated by trusted updater + DNS)
This commit is contained in:
@@ -31,15 +31,15 @@ on Gitea.
|
||||
|
||||
## Scenarios in the catalog
|
||||
|
||||
| id | mode | source |
|
||||
| ----------- | --------- | ------------------------------------------------------------------- |
|
||||
| procurement | live | Purchase Requisition → PO (`pr_to_po_def` on demo.flow-master.ai) |
|
||||
| extra-1 | live | Atlas F1 Fresh (procurement variant) |
|
||||
| extra-2 | live | Atlas F1 Fresh (procurement variant) |
|
||||
| ar | blueprint | AR · Customer Refund Approval |
|
||||
| hcm | blueprint | HCM · New Hire Onboarding |
|
||||
| gl | blueprint | GL · Period-End Close |
|
||||
| service | blueprint | Service Ops · Customer Incident |
|
||||
| id | mode | source |
|
||||
|-------------|-----------|-------------------------------------------------------------------|
|
||||
| procurement | live | Purchase Requisition → PO (`pr_to_po_def` on demo.flow-master.ai) |
|
||||
| extra-1 | live | Atlas F1 Fresh (procurement variant) |
|
||||
| extra-2 | live | Atlas F1 Fresh (procurement variant) |
|
||||
| ar | blueprint | AR · Customer Refund Approval |
|
||||
| hcm | blueprint | HCM · New Hire Onboarding |
|
||||
| gl | blueprint | GL · Period-End Close |
|
||||
| service | blueprint | Service Ops · Customer Incident |
|
||||
|
||||
**Live** = backed by a real EA2 process definition currently in the demo
|
||||
backend, with real runtime transactions and a real work-item queue.
|
||||
@@ -68,15 +68,20 @@ prior version. Safeguards now in place:
|
||||
## Live-mode mechanics (the CORS gotcha)
|
||||
|
||||
`demo.flow-master.ai` does not advertise CORS headers for arbitrary origins.
|
||||
`src/lib/api.ts` therefore uses an **empty `baseUrl` everywhere** (both dev
|
||||
and prod). All `/api/*` requests are same-origin from the browser's
|
||||
perspective; whatever is serving the page is responsible for proxying them
|
||||
to the backend.
|
||||
|
||||
- In **dev** (`pnpm dev`): Vite proxies `/api/*` to
|
||||
`${VITE_FM_BASE:-https://demo.flow-master.ai}` (see `vite.config.ts`). The
|
||||
browser sees a same-origin request, no CORS check. `src/lib/api.ts` uses an
|
||||
empty `baseUrl` in dev for this.
|
||||
- In **production**: deploy the build at the same origin as the backend (or
|
||||
behind a reverse proxy that passes `/api/*` through). Cross-origin
|
||||
deployments fail gracefully — `setMode("live")` catches the error, surfaces
|
||||
it in the topbar banner and a toast, and falls back to snapshot mode.
|
||||
- **Dev** (`pnpm dev`): `vite.config.ts` proxies `/api/*` to
|
||||
`${VITE_FM_BASE:-https://demo.flow-master.ai}`.
|
||||
- **Prod** (Docker image): the bundled `nginx.conf` reverse-proxies `/api/*`
|
||||
to `https://demo.flow-master.ai`. The image is intended to sit behind the
|
||||
`mc.flow-master.ai` ingress (see `FM06/flowmaster-ops` overlay).
|
||||
- **Anywhere else**: set `VITE_FM_BASE=https://your-backend` at build time
|
||||
and accept that browsers will reject the cross-origin call. Live mode then
|
||||
fails gracefully — `setMode("live")` catches the error, raises an
|
||||
`mc-banner-err` banner + error toast, and falls back to snapshot.
|
||||
|
||||
## Run, test, build
|
||||
|
||||
@@ -129,15 +134,25 @@ fetch_scenarios.mjs # Node script to refresh src/scenarios.json
|
||||
|
||||
## Deploy
|
||||
|
||||
Production deployment is tracked in `FM06/flowmaster-ops` under
|
||||
`manifests/overlays/mc.flow-master.ai/`. A static nginx serves `dist/`;
|
||||
ingress fronts it at `mc.flow-master.ai`. `/api/*` is reverse-proxied to the
|
||||
same backend `demo.flow-master.ai` talks to — that's what makes live mode
|
||||
work in production without CORS.
|
||||
Production deployment is tracked in
|
||||
[FM06/flowmaster-ops PR #1164](https://gitea.flow-master.ai/FM06/flowmaster-ops/pulls/1164),
|
||||
which adds three resources to `manifests/overlays/demo/`:
|
||||
|
||||
- `mc-deployment.yaml` — 2-replica nginx Deployment in the `demo` namespace
|
||||
- `mc-service.yaml` — ClusterIP service on port 80
|
||||
- `mc-ingress.yaml` — Traefik ingress at `mc.flow-master.ai` with cert-manager DNS-01 cert
|
||||
|
||||
**Status:** the PR is open and mergeable. The live URL `https://mc.flow-master.ai` is **not yet serving** — two pre-merge action items remain for the trusted updater:
|
||||
|
||||
1. Cloudflare A record `mc.flow-master.ai → 65.21.71.186, 91.98.159.56` (same as `hakeem.flow-master.ai`).
|
||||
2. Gitea Actions must have published the first image tag (`gitea.flow-master.ai/shad/mission-control-demo:sha-…`) — `mc-deployment.yaml` pins that explicit SHA, not `:latest`.
|
||||
|
||||
Until the PR merges + DNS is added, the artifact is this repo + the open PR.
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
# dist/ is the static site → ship to nginx referenced by the ops overlay
|
||||
# dist/ is the static site. The Dockerfile bakes it into a tiny nginx
|
||||
# image. CI in .gitea/workflows/build.yml builds + publishes on push to main.
|
||||
```
|
||||
|
||||
## What's intentionally not here
|
||||
|
||||
Reference in New Issue
Block a user