Files
agentic-mobile-control/cloud-console
q792602257andClaude Opus 4.6 2ccbc63d95 feat(cloud-api): bake cloud-console SPA into the image and serve at /console
Multi-stage Dockerfile: stage 1 (node:20-bookworm-slim) builds cloud-console
with vite base "/console/"; stage 2 (uv) copies dist/ to /app/console-static.
Cloud API mounts the SPA at /console via SpaStaticFiles (StaticFiles subclass
that falls back to index.html for deep-link refreshes) when the new
CLOUD_CONSOLE_STATIC_DIR env is set, and 307-redirects / to /console/. Static
files bypass bearer auth (the SPA shell is public; tokens are still required
for /v1/*). Compose enables the mount by default; local dev still uses
npm run dev + CLOUD_CONSOLE_CORS_ORIGINS.

Jenkinsfile passes mirror overrides (NODE_IMAGE, NPM_REGISTRY, UV_IMAGE,
APT_MIRROR, UV_INDEX_URL) as --build-arg, defaulting to CN mirrors
(registry.jerryyan.net, registry.npmmirror.com, registry-ghcr.jerryyan.top,
mirrors.aliyun.com) so CN builds don't time out; Dockerfile ARGs default to
official upstreams so `docker build .` still works anywhere.

Backend suite: 443 passed (-m "not integration"); cloud-console typecheck
and production build succeed with the new base path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 14:40:52 +08:00
..

Cloud Console

Independent Vue 3 + Vite single-page app for the Cloud Control Plane (apps/cloud-api). Operators authenticate by pasting a pre-issued scoped bearer token; the console stores it in sessionStorage, attaches Authorization: Bearer <token> to every request, and clears it whenever the Cloud API responds 401 or 403.

The app talks only to the platform SDK surface (/v1/...) and consumes the two listing endpoints added by the cloud-console change (GET /v1/tasks, GET /v1/tasks/{task_id}/attempts) alongside the existing /v1/devices, /v1/hosts, /v1/plugins, and POST /v1/plugins routes.

Prerequisites

  • Node.js 20+ (matching the existing console/ SPA project)
  • A running Cloud API (apps/cloud-api) reachable from your browser
  • A bearer token issued via CLOUD_PUBLIC_CREDENTIALS_JSON whose scopes cover what you intend to do from the console. Recommended least-privilege set:
    • tasks:read — task list and attempt history views
    • pool:read — device and host views
    • plugins:read — plugin list
    • Add tasks:submit/plugins:admin only if you need the write actions from the same tab.

Configure the backend CORS allow-list

The Cloud API has no CORS middleware by default. Before a browser can call it cross-origin, set CLOUD_CONSOLE_CORS_ORIGINS to a comma-separated allow-list that includes the exact origin your dev server prints (scheme + host + port — no trailing slash):

# Example: allow the default Vite dev origin
export CLOUD_CONSOLE_CORS_ORIGINS="http://127.0.0.1:5173"

Restart apps/cloud-api after changing this env. Tokens are still required — the allow-list only says which browser origins may send them.

Run the dev server

cd cloud-console
cp .env.example .env.local
# Edit .env.local if your Cloud API is not at http://127.0.0.1:8001
npm install
npm run dev

Vite prints a local URL (default http://127.0.0.1:5173). Open it, paste a bearer token, and the task/device/host/plugin dashboards become available.

.env.local overrides the default base URL via VITE_CLOUD_API_BASE_URL (defaults to http://127.0.0.1:8001).

Build for production

npm run build      # type-checks with vue-tsc, then emits dist/
npm run preview    # serves the built bundle locally

dist/ is a static bundle — host it behind any static file server or CDN and point it at a deployed Cloud API via VITE_CLOUD_API_BASE_URL set at build time.

Token handling

  • The token is held in sessionStorage only. Closing the tab discards it.
  • Every API request attaches Authorization: Bearer <token> and targets only the configured VITE_CLOUD_API_BASE_URL.
  • A 401/403 response clears the stored token and returns the operator to the token-entry screen with the API's error detail.

Project layout

cloud-console/
├── src/
│   ├── api.ts               # API client wrapper (token storage, fetch, errors)
│   ├── types.ts             # TS interfaces mirroring the REST models
│   ├── App.vue              # Shell: token gate, nav, view router
│   ├── main.ts              # Vue bootstrap
│   ├── style.css            # Dark theme styles
│   └── views/
│       ├── TokenScreen.vue
│       ├── TasksView.vue        # list + detail with attempt history
│       ├── DevicesView.vue      # device pool + host registry
│       └── PluginsView.vue      # registry list + registration form
├── index.html
├── package.json
├── tsconfig.json / tsconfig.node.json
└── vite.config.ts