nexus/ui/src/components/onboarding/PuterAuthButton.tsx
Nexus Dev 3796de8493 feat(31-03): add puter-proxy API client and auth/key entry components
- puterProxyApi: storeToken, getAuthUrl, claimGoogleTokens, storeApiKey
- PuterAuthButton: loads Puter CDN script, triggers signIn popup, captures token
- GoogleOAuthButton: 3-second risk warning gate, opens OAuth popup, captures stateId
- ApiKeyEntryForm: provider dropdown (OpenAI/Anthropic/Groq) + password input
2026-04-03 00:39:55 +00:00

105 lines
2.9 KiB
TypeScript

// [nexus] Puter auth button — loads CDN script, triggers signIn popup, captures token
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { CheckCircle, LogIn } from "lucide-react";
interface PuterAuthButtonProps {
onSuccess: (token: string) => void;
onError: (msg: string) => void;
}
function loadScript(): Promise<void> {
return new Promise((resolve, reject) => {
if ((window as any).puter) {
resolve();
return;
}
const script = document.createElement("script");
script.src = "https://js.puter.com/v2/";
script.onload = () => resolve();
script.onerror = () => reject(new Error("Failed to load Puter SDK"));
document.head.appendChild(script);
});
}
export function PuterAuthButton({ onSuccess, onError }: PuterAuthButtonProps) {
const [loading, setLoading] = useState(false);
const [connected, setConnected] = useState(false);
async function handleClick() {
setLoading(true);
try {
await loadScript();
// signIn must be called in the same async chain as the click event for popup to work
await (window as any).puter.auth.signIn();
// Extract token — try multiple access patterns (Pitfall 1: token location varies)
let token: string | undefined =
(window as any).puter?.authToken ??
(window as any).puter?.auth?.token;
if (!token) {
const user = await (window as any).puter?.auth?.getUser?.();
token = user?.token;
}
if (!token) {
console.warn("[nexus] Puter token is undefined after signIn — user may not be authenticated");
}
onSuccess(token ?? "");
setConnected(true);
} catch {
onError("Puter sign-in failed. Check your Puter.com account and try again.");
} finally {
setLoading(false);
}
}
return (
<Button
type="button"
onClick={handleClick}
disabled={loading || connected}
aria-busy={loading}
className="w-full"
>
{loading ? (
<span className="flex items-center gap-2">
<svg
className="h-4 w-4 animate-spin"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
Connecting to Puter...
</span>
) : connected ? (
<span className="flex items-center gap-2">
<CheckCircle className="h-4 w-4" />
Puter connected
</span>
) : (
<span className="flex items-center gap-2">
<LogIn className="h-4 w-4" />
Continue with Puter
</span>
)}
</Button>
);
}