From 8d6384314b6e5f018ab4ff47689c9eddf37d9c77 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:13:40 +0800 Subject: [PATCH] fix(cli): redirect Console on a bare/reserved platform host to the cloud console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `objectstack serve`/`dev` serves the Console SPA on ANY host, but a bare/reserved platform host (the apex, or `www`/`app`/… — none bind a tenant env) has no env, so its `/api/v1/auth/*` calls 404 → the login renders the misleading "Auth request failed with status 404" dead form. The unknown-hostname guard already passed these reserved hosts straight through (RESERVED includes the empty/apex subdomain), so the SPA loaded and broke. Fix: in the guard, when a reserved/apex platform host receives a `/_console*` request AND the runtime is cloud-connected (`OS_CLOUD_URL` set), 302 it to the cloud control plane (`OS_CLOUD_URL` + `/_console/`), where the user picks/opens an environment. A self-hosted single-env runtime (no `OS_CLOUD_URL`) keeps the prior pass-through, and non-console paths (infra, health, /api) are unchanged. A real env host resolves to a tenant and is served normally. Mirrors the same cleanup on the cloud serve-node path (objectos `server/index.ts`), which was verified live: bare `/_console/login` → 302 → cloud console; `/api/v1/auth/*` still 404; env host /_console → 200. cli build green (DTS typecheck clean). Co-Authored-By: Claude Opus 4.8 --- packages/cli/src/commands/serve.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index bd976682e..db7889a46 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -859,8 +859,23 @@ export default class Serve extends Command { if (!isPlatformHost) return next(); const sub = host === __rootDomain ? '' : host.slice(0, -(__rootDomain.length + 1)); const head = sub.split('.').pop() || ''; - if (RESERVED.has(sub) || RESERVED.has(head)) return next(); const p = c.req.path; + if (RESERVED.has(sub) || RESERVED.has(head)) { + // A browser loading the Console on a bare/reserved platform host + // (the apex or `www`/`app`/… — none bind a tenant env) gets the + // Console SPA, but its `/api/v1/auth/*` calls 404 (no env → no + // auth) → a dead "Auth request failed with status 404" login. + // When this runtime is cloud-connected (`OS_CLOUD_URL` set), send + // Console requests to the cloud control plane to pick/open an + // environment instead. A self-hosted single-env runtime (no + // `OS_CLOUD_URL`) keeps the prior pass-through. Non-console paths + // (infra, health, /api) fall through below unchanged. + const cloudUrl = (process.env.OS_CLOUD_URL || '').trim(); + if (cloudUrl && (p === '/_console' || p.startsWith('/_console/'))) { + return c.redirect(`${cloudUrl.replace(/\/+$/, '')}/_console/`, 302); + } + return next(); + } if (p.startsWith('/_admin/') || p === '/_admin' || p.startsWith('/.well-known/')) { return next(); }