233 lines
7.2 KiB
TypeScript
233 lines
7.2 KiB
TypeScript
"use client";
|
|
|
|
import { useSession } from "next-auth/react";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import React, { useEffect, Suspense } from "react";
|
|
import NextAuthComponent from "@/app/components/NextAuthComponent";
|
|
|
|
import "../styles/new-site.css";
|
|
|
|
function deriveWorkspace(email: string): string {
|
|
return (
|
|
email
|
|
.split("@")[0]
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "-") + "-account"
|
|
);
|
|
}
|
|
|
|
function AuthPageInner() {
|
|
const { data: session, status } = useSession();
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
|
|
const [ssoProcessing, setSsoProcessing] = React.useState(false);
|
|
const [ssoToken, setSsoToken] = React.useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (status === "authenticated" && session?.user?.email) {
|
|
const isVibnCodeSSO = searchParams?.get("vibncode") === "true";
|
|
|
|
if (isVibnCodeSSO) {
|
|
setSsoProcessing(true);
|
|
// Call our new secure token endpoint
|
|
fetch("/api/auth/token")
|
|
.then((r) => r.json())
|
|
.then((data) => {
|
|
if (data.token) {
|
|
setSsoToken(data.token);
|
|
// Deep-link redirect back to the VibnCode desktop app
|
|
window.location.href = `vibncode://auth/callback?token=${data.token}`;
|
|
} else {
|
|
console.error("SSO Token missing from response", data);
|
|
setSsoProcessing(false);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error("Desktop SSO failed:", err);
|
|
setSsoProcessing(false);
|
|
});
|
|
return;
|
|
}
|
|
|
|
const workspace = deriveWorkspace(session.user.email);
|
|
|
|
// Check if user has projects. If 0, go to onboarding, else go to projects.
|
|
fetch("/api/projects")
|
|
.then((r) => r.json())
|
|
.then((d) => {
|
|
if (d.projects && d.projects.length > 0) {
|
|
router.push(`/${workspace}/projects`);
|
|
} else {
|
|
router.push(`/onboarding`);
|
|
}
|
|
})
|
|
.catch(() => router.push(`/${workspace}/projects`));
|
|
}
|
|
}, [status, session, router, searchParams]);
|
|
|
|
if (status === "loading" || ssoProcessing) {
|
|
const deepLink = ssoToken ? `vibncode://auth/callback?token=${ssoToken}` : "";
|
|
|
|
return (
|
|
<div
|
|
className="new-site-wrapper"
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
minHeight: "100vh",
|
|
background: "radial-gradient(circle at 20% 20%, #1c1c1f, #0b0b0f 60%)",
|
|
}}
|
|
>
|
|
{ssoToken ? (
|
|
<div
|
|
style={{
|
|
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
background: "rgba(12, 12, 16, 0.85)",
|
|
borderRadius: "20px",
|
|
padding: "32px",
|
|
boxShadow: "0 18px 50px rgba(0, 0, 0, 0.35)",
|
|
backdropFilter: "blur(16px)",
|
|
textAlign: "center",
|
|
width: "min(480px, 90vw)",
|
|
color: "#f5f5f5",
|
|
fontFamily: "-apple-system, sans-serif",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
display: "inline-flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
width: "56px",
|
|
height: "56px",
|
|
borderRadius: "50%",
|
|
border: "1px solid rgba(255, 255, 255, 0.12)",
|
|
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02))",
|
|
fontSize: "28px",
|
|
marginBottom: "20px",
|
|
}}
|
|
>
|
|
✓
|
|
</div>
|
|
<h1 style={{ margin: "0 0 12px", fontSize: "24px", fontWeight: "600", color: "#f8f8f8" }}>
|
|
Authentication Successful
|
|
</h1>
|
|
<p style={{ margin: "0 0 24px", color: "#cfcfd4", fontSize: "14px" }}>
|
|
Signed in. Redirecting to VibnCode...
|
|
</p>
|
|
<div
|
|
style={{
|
|
margin: "0 auto 20px",
|
|
width: "44px",
|
|
height: "44px",
|
|
borderRadius: "50%",
|
|
border: "4px solid rgba(255, 255, 255, 0.15)",
|
|
borderTopColor: "#ffffff",
|
|
animation: "spin 1s linear infinite",
|
|
}}
|
|
/>
|
|
<style>{`
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
`}</style>
|
|
<p style={{ margin: "0 0 16px", color: "#b6b6bd", lineHeight: "1.6", fontSize: "13px" }}>
|
|
If the app doesn't open automatically, copy your Workspace API Key below and paste it into the connection card.
|
|
</p>
|
|
<div style={{ display: "flex", gap: "10px", justifyContent: "center", marginBottom: "16px" }}>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(ssoToken);
|
|
alert("Workspace API Key copied!");
|
|
}}
|
|
style={{
|
|
padding: "10px 16px",
|
|
borderRadius: "999px",
|
|
border: "1px solid rgba(255, 255, 255, 0.18)",
|
|
background: "rgba(255, 255, 255, 0.12)",
|
|
color: "#ffffff",
|
|
cursor: "pointer",
|
|
fontSize: "13px",
|
|
fontWeight: "500",
|
|
}}
|
|
>
|
|
Copy Workspace Key
|
|
</button>
|
|
</div>
|
|
<div
|
|
style={{
|
|
padding: "12px",
|
|
borderRadius: "12px",
|
|
background: "rgba(255, 255, 255, 0.06)",
|
|
fontFamily: "monospace",
|
|
fontSize: "12px",
|
|
wordBreak: "break-all",
|
|
color: "#d8d8df",
|
|
}}
|
|
>
|
|
{ssoToken}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
gap: "16px",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
width: "24px",
|
|
height: "24px",
|
|
borderRadius: "50%",
|
|
border: "2px solid oklch(0.20 0.009 60)",
|
|
borderTopColor: "var(--accent)",
|
|
animation: "spin .9s linear infinite",
|
|
}}
|
|
/>
|
|
<style>{`
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
`}</style>
|
|
<div
|
|
style={{
|
|
color: "var(--fg-mute)",
|
|
fontFamily: "var(--font-mono)",
|
|
fontSize: "11px",
|
|
letterSpacing: "0.1em",
|
|
textTransform: "uppercase",
|
|
}}
|
|
>
|
|
Checking session
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="new-site-wrapper"
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
minHeight: "100vh",
|
|
}}
|
|
>
|
|
<NextAuthComponent />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function AuthPage() {
|
|
return (
|
|
<Suspense>
|
|
<AuthPageInner />
|
|
</Suspense>
|
|
);
|
|
}
|