integrate backend Google OAuth and set up avatar media proxy
- Implement the full login and onboarding workflow to handle name, handle, and avatar. - Rewrite avatar asset URLs to route through the self-hosted media service. - Add Next.js rewrites configuration to proxy media requests directly to the FastAPI backend.
This commit is contained in:
+69
-59
@@ -8,31 +8,33 @@ export default function LoginPage() {
|
||||
|
||||
const handleGoogleLogin = () => {
|
||||
setIsLoading(true);
|
||||
// 指向 FastAPI 的 Google OAuth 入口
|
||||
// 登录成功后,FastAPI 会重定向回 http://localhost:3000/ (即你的根目录分流器)
|
||||
window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/login/google`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white flex flex-col">
|
||||
<div className="min-h-screen bg-white flex flex-col font-sans">
|
||||
{/* Header */}
|
||||
<div className="border-b border-gray-200">
|
||||
<header className="border-b border-gray-100 bg-white/80 backdrop-blur-md sticky top-0 z-10">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-pink-600 to-purple-600 rounded-lg flex items-center justify-center">
|
||||
<Link href="/" className="flex items-center gap-2 w-fit">
|
||||
<div className="w-8 h-8 bg-linear-to-br from-pink-600 to-purple-600 rounded-lg flex items-center justify-center shadow-xs">
|
||||
<span className="text-white text-sm font-bold">RP</span>
|
||||
</div>
|
||||
<span className="font-semibold text-lg">RoleplayAI</span>
|
||||
<span className="font-bold text-lg tracking-tight text-gray-900">RoleplayAI</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<main className="flex-1 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="space-y-8">
|
||||
<div className="bg-white rounded-2xl space-y-8">
|
||||
{/* Welcome Section */}
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-4xl font-bold">Welcome Back</h1>
|
||||
<p className="text-gray-600">Sign in to continue to RoleplayAI</p>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight text-gray-900">Welcome Back</h1>
|
||||
<p className="text-gray-500 font-medium">Sign in to continue your adventure</p>
|
||||
</div>
|
||||
|
||||
{/* Google OAuth Button */}
|
||||
@@ -40,95 +42,103 @@ export default function LoginPage() {
|
||||
<button
|
||||
onClick={handleGoogleLogin}
|
||||
disabled={isLoading}
|
||||
className="w-full border-2 border-gray-200 rounded-xl py-3 px-4 font-medium hover:bg-gray-50 transition flex items-center justify-center gap-3 disabled:opacity-50"
|
||||
className="w-full border-2 border-gray-200 rounded-xl py-3.5 px-4 font-semibold hover:bg-gray-50 hover:border-gray-300 transition-all flex items-center justify-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed group"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032 c0-3.331,2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.461,2.268,15.365,1,12.545,1 C6.477,1,1.54,5.952,1.54,12s4.938,11,11.005,11c6.071,0,11.067-4.975,11.067-11c0-0.713-0.092-1.405-0.196-2.067H12.545z"
|
||||
/>
|
||||
</svg>
|
||||
{isLoading ? "Signing in..." : "Continue with Google"}
|
||||
{!isLoading ? (
|
||||
<>
|
||||
<svg className="w-5 h-5 group-hover:scale-110 transition-transform" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032 c0-3.331,2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.461,2.268,15.365,1,12.545,1 C6.477,1,1.54,5.952,1.54,12s4.938,11,11.005,11c6.071,0,11.067-4.975,11.067-11c0-0.713-0.092-1.405-0.196-2.067H12.545z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Continue with Google</span>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 border-2 border-gray-300 border-t-black rounded-full animate-spin" />
|
||||
<span>Redirecting...</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-200"></div>
|
||||
<div className="w-full border-t border-gray-100"></div>
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="px-2 bg-white text-gray-500">or</span>
|
||||
<div className="relative flex justify-center text-xs uppercase font-bold tracking-widest">
|
||||
<span className="px-4 bg-white text-gray-400">or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email Form */}
|
||||
<form className="space-y-4">
|
||||
{/* Email Form (Disabled) */}
|
||||
<form className="space-y-4" onSubmit={(e) => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium mb-2">
|
||||
<label htmlFor="email" className="block text-sm font-bold text-gray-700 mb-2">
|
||||
Email Address
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
placeholder="you@example.com"
|
||||
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-black focus:outline-none transition"
|
||||
className="w-full px-4 py-3 border-2 border-gray-100 rounded-xl bg-gray-50/50 text-gray-400 cursor-not-allowed outline-none"
|
||||
disabled
|
||||
/>
|
||||
<p className="text-xs text-gray-500 mt-2">Email login coming soon</p>
|
||||
<div className="mt-3 flex items-center gap-2 text-amber-600 bg-amber-50 px-3 py-2 rounded-lg border border-amber-100">
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider">Coming Soon</span>
|
||||
<span className="text-xs font-medium">Email login is currently in beta</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Agreement Text */}
|
||||
<p className="text-center text-sm text-gray-600">
|
||||
<p className="text-center text-xs text-gray-500 leading-relaxed">
|
||||
By signing in, you agree to our{" "}
|
||||
<a href="#" className="text-black font-medium hover:underline">
|
||||
<a href="#" className="text-gray-900 font-bold hover:underline underline-offset-4">
|
||||
Terms of Service
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a href="#" className="text-black font-medium hover:underline">
|
||||
<a href="#" className="text-gray-900 font-bold hover:underline underline-offset-4">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</p>
|
||||
|
||||
{/* Footer Links */}
|
||||
<div className="space-y-3 text-center text-sm">
|
||||
<p>
|
||||
Don't have an account?{" "}
|
||||
<a href="#" className="text-black font-medium hover:underline">
|
||||
Sign up
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/" className="text-gray-600 hover:text-black">
|
||||
Back to Home
|
||||
</a>
|
||||
<div className="pt-6 border-t border-gray-100 space-y-4 text-center">
|
||||
<p className="text-sm text-gray-600">
|
||||
Don't have an account?{" "}
|
||||
<Link href="/" className="text-pink-600 font-bold hover:text-pink-700 transition-colors">
|
||||
Sign up for free
|
||||
</Link>
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-block text-sm font-semibold text-gray-400 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
← Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trust Indicators */}
|
||||
<div className="mt-12 pt-8 border-t border-gray-200 space-y-6">
|
||||
<p className="text-center text-xs text-gray-500">TRUSTED BY</p>
|
||||
<div className="flex justify-center items-center gap-6 text-gray-400">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold text-gray-600">10K+</div>
|
||||
<p className="text-xs">Users</p>
|
||||
</div>
|
||||
<div className="w-px h-8 bg-gray-200"></div>
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold text-gray-600">1000+</div>
|
||||
<p className="text-xs">Characters</p>
|
||||
</div>
|
||||
<div className="w-px h-8 bg-gray-200"></div>
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold text-gray-600">24/7</div>
|
||||
<p className="text-xs">Support</p>
|
||||
</div>
|
||||
<div className="mt-12 grid grid-cols-3 gap-4 border-t border-gray-100 pt-8">
|
||||
<div className="text-center">
|
||||
<div className="text-xl font-bold text-gray-900">10K+</div>
|
||||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-tighter">Active Users</p>
|
||||
</div>
|
||||
<div className="text-center border-x border-gray-100">
|
||||
<div className="text-xl font-bold text-gray-900">1000+</div>
|
||||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-tighter">AI Models</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xl font-bold text-gray-900">24/7</div>
|
||||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-tighter">Fast Support</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user