fix: enable canvas dev login
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled

This commit is contained in:
Kua Agent
2026-06-16 10:24:27 +04:00
parent 99203267aa
commit 76ce4c23b2
4 changed files with 42 additions and 9 deletions
+9
View File
@@ -85,6 +85,15 @@ server {
proxy_buffering off;
}
# Dev/demo auth discovery. The SPA uses this to decide whether to show and
# enable the dev-login affordance. Keep this exact route ahead of the SPA
# fallback or nginx will return index.html and the client will disable it.
location = /internal/dev-login-config {
default_type application/json;
add_header Cache-Control "no-store" always;
return 200 '{"enabled":true,"email":"dev@flow-master.ai"}';
}
# SPA fallback to index.html for client-side routing.
location / {
try_files $uri $uri/ /index.html;
+3 -3
View File
@@ -244,13 +244,13 @@ export const api = {
return data;
},
async devLoginConfig(signal?: AbortSignal): Promise<{ enabled: boolean | null }> {
async devLoginConfig(signal?: AbortSignal): Promise<{ enabled: boolean | null; email?: string }> {
try {
const init: RequestInit = { method: "GET", headers: { "Accept": "application/json" }, signal };
const r = await instrumentedFetch("GET", `${this.config.baseUrl}/internal/dev-login-config`, init);
if (r.ok) {
const body = await r.json() as { enabled: boolean };
return { enabled: !!body.enabled };
const body = await r.json() as { enabled: boolean; email?: string };
return { enabled: !!body.enabled, email: body.email };
}
} catch {
// network/parse error — caller treats null as "no opinion"
+16
View File
@@ -71,6 +71,22 @@ describe("Login Scene", () => {
});
});
it("uses configured dev-login email without requiring typed credentials", async () => {
(api.devLoginConfig as any).mockResolvedValueOnce({ enabled: true, email: "dev@flow-master.ai" });
mockLoginAs.mockResolvedValueOnce(undefined);
render(<Login />);
const devButton = await screen.findByRole("button", { name: /DEV-LOGIN/i });
expect((devButton as HTMLButtonElement).disabled).toBe(false);
fireEvent.click(devButton);
await waitFor(() => {
expect(mockLoginAs).toHaveBeenCalledWith("dev@flow-master.ai", undefined, { method: "dev" });
expect(mockSetScene).toHaveBeenCalledWith("mission");
});
});
it("SSO button is disabled until probe resolves to ready", async () => {
render(<Login />);
const ssoBtn = screen.getAllByRole("button", { name: /MICROSOFT SIGN-IN|CONTINUE WITH MICROSOFT/i })[0] as HTMLButtonElement;
+13 -5
View File
@@ -11,6 +11,7 @@ export default function Login() {
const [rememberMe, setRememberMe] = useState(false);
const buildAllowsDev = (import.meta.env.VITE_ENABLE_DEV_LOGIN ?? "true") !== "false";
const [devLoginEnabled, setDevLoginEnabled] = useState(buildAllowsDev);
const [devLoginEmail, setDevLoginEmail] = useState("dev@flow-master.ai");
const [ssoState, setSsoState] = useState<"unknown" | "ready" | "not-configured" | "not-wired">("unknown");
const [backendState, setBackendState] = useState<"unknown" | "up" | "auth-down" | "proxy-down">("unknown");
const [loading, setLoading] = useState(false);
@@ -21,7 +22,13 @@ export default function Login() {
setDevLoginEnabled(false);
} else {
api.devLoginConfig()
.then((cfg) => { if (cfg.enabled === false) setDevLoginEnabled(false); })
.then((cfg) => {
if (cfg.enabled === false) setDevLoginEnabled(false);
if (cfg.email) {
setDevLoginEmail(cfg.email);
setEmail((current) => current || cfg.email || "");
}
})
.catch(() => { /* endpoint absent → keep state */ });
}
fetch("/api/v1/auth/microsoft/login", { method: "GET", redirect: "manual" })
@@ -69,14 +76,15 @@ export default function Login() {
setError("Developer sign-in is disabled in this build.");
return;
}
if (!email) {
setError("Email required for dev-login");
const selectedEmail = email || devLoginEmail;
if (!selectedEmail) {
setError("Developer sign-in has no configured email.");
return;
}
setLoading(true);
setError(null);
try {
await loginAs(email, undefined, { method: "dev" });
await loginAs(selectedEmail, undefined, { method: "dev" });
setScene("mission");
} catch (err) {
setError((err as Error).message);
@@ -211,7 +219,7 @@ export default function Login() {
</button>
{devLoginEnabled && (
<button type="button" className="dev-login-btn" onClick={handleDevLogin} disabled={loading || !email}>
<button type="button" className="dev-login-btn" onClick={handleDevLogin} disabled={loading}>
<Bot size={14} /> SIGN IN AS DEVELOPER (DEV-LOGIN)
</button>
)}