LoginGate now shows CreateProfile (display name only) instead of App when an authenticated session has no provisioned geniusrun profile yet, backed by the new POST /api/setup endpoint and session/me's has_profile flag.
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { api, BASE_URL } from "./api/client";
|
||
import "./LoginGate.css";
|
||
import App from "./App";
|
||
import { CreateProfile } from "./CreateProfile";
|
||
import type { SessionInfo } from "./types/api";
|
||
|
||
type Status = "loading" | "authenticated" | "unauthenticated";
|
||
|
||
const AUTH_ERROR_MESSAGES: Record<string, string> = {
|
||
forbidden: "Your account isn't authorized for geniusrun.",
|
||
failed: "Login failed, please try again.",
|
||
};
|
||
|
||
// Wraps App: on mount, asks the backend whether this browser already has a
|
||
// valid session (GET /api/session/me). geniusrun has no anonymous view, so
|
||
// this is the first fork -- "show the login screen" vs "show the app." A
|
||
// second fork, once authenticated, is whether the session's account has a
|
||
// provisioned profile yet (session.has_profile) -- a brand-new OIDC login
|
||
// sees CreateProfile instead of App until it submits one.
|
||
export function LoginGate() {
|
||
const [status, setStatus] = useState<Status>("loading");
|
||
const [session, setSession] = useState<SessionInfo | null>(null);
|
||
|
||
useEffect(() => {
|
||
api
|
||
.getSessionInfo()
|
||
.then((s) => {
|
||
setSession(s);
|
||
setStatus("authenticated");
|
||
})
|
||
.catch(() => setStatus("unauthenticated"));
|
||
}, []);
|
||
|
||
if (status === "loading") {
|
||
return <div className="login-gate-loading">Loading…</div>;
|
||
}
|
||
|
||
if (status === "unauthenticated") {
|
||
const authError = new URLSearchParams(window.location.search).get("auth_error");
|
||
return (
|
||
<div className="login-gate">
|
||
<h1>🧞♀️ geniusrun</h1>
|
||
{authError && <p className="login-gate-error">{AUTH_ERROR_MESSAGES[authError] ?? "Login failed, please try again."}</p>}
|
||
<a className="login-gate-button" href={`${BASE_URL}/api/session/login`}>
|
||
Log in
|
||
</a>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!session!.has_profile) {
|
||
return (
|
||
<CreateProfile
|
||
onCreated={(displayName) => setSession((s) => (s ? { ...s, has_profile: true, display_name: displayName } : s))}
|
||
/>
|
||
);
|
||
}
|
||
|
||
return <App session={session!} />;
|
||
}
|