184 lines
7.3 KiB
TypeScript
184 lines
7.3 KiB
TypeScript
|
|
"use client";
|
|||
|
|
|
|||
|
|
import { client } from "@/lib/auth-client";
|
|||
|
|
import { Button } from "@nice/ui/components/button";
|
|||
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@nice/ui/components/card";
|
|||
|
|
import { Input } from "@nice/ui/components/input";
|
|||
|
|
import { Label } from "@nice/ui/components/label";
|
|||
|
|
import { toast } from "@nice/ui/components/sonner";
|
|||
|
|
import { useState } from "react";
|
|||
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|||
|
|
|
|||
|
|
export default function SignInPage() {
|
|||
|
|
const [credentials, setCredentials] = useState({
|
|||
|
|
email: "test@example.com",
|
|||
|
|
password: "password123"
|
|||
|
|
});
|
|||
|
|
const [isLoading, setIsLoading] = useState(false);
|
|||
|
|
const router = useRouter();
|
|||
|
|
const searchParams = useSearchParams();
|
|||
|
|
// 检查是否是从 OIDC Provider 重定向过来的
|
|||
|
|
const isOIDCFlow = searchParams.get('response_type') === 'code' || searchParams.get('client_id');
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
const handleSignIn = async (e: React.FormEvent) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
setIsLoading(true);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await client.signIn.email({
|
|||
|
|
email: credentials.email,
|
|||
|
|
password: credentials.password,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (result.data) {
|
|||
|
|
toast.success("登录成功!");
|
|||
|
|
|
|||
|
|
// 如果是 OIDC 流程,重定向回原始请求
|
|||
|
|
if (isOIDCFlow) {
|
|||
|
|
// 获取所有查询参数并重建 URL
|
|||
|
|
const params = new URLSearchParams(window.location.search);
|
|||
|
|
const redirectUrl = `http://localhost:3001/api/auth/oauth2/authorize?${params.toString()}`;
|
|||
|
|
window.location.href = redirectUrl;
|
|||
|
|
} else {
|
|||
|
|
router.push('/dashboard');
|
|||
|
|
}
|
|||
|
|
} else if (result.error) {
|
|||
|
|
toast.error(`登录失败: ${result.error.message}`);
|
|||
|
|
}
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("登录失败:", error);
|
|||
|
|
toast.error(`登录失败: ${error.message || "未知错误"}`);
|
|||
|
|
} finally {
|
|||
|
|
setIsLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleCreateTestUser = async () => {
|
|||
|
|
setIsLoading(true);
|
|||
|
|
try {
|
|||
|
|
// 使用更详细的用户信息
|
|||
|
|
const result = await client.signUp.email({
|
|||
|
|
email: credentials.email,
|
|||
|
|
password: credentials.password,
|
|||
|
|
name: "Test User",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (result.data) {
|
|||
|
|
toast.success("测试用户创建成功!可以使用此账户登录");
|
|||
|
|
} else if (result.error) {
|
|||
|
|
// 显示更详细的错误信息
|
|||
|
|
console.error("注册错误详情:", result.error);
|
|||
|
|
|
|||
|
|
// 检查是否是重定向URI错误(这通常不应该出现在注册流程中)
|
|||
|
|
if (result.error.code === 'INVALID_REDIRECT_URI') {
|
|||
|
|
toast.error("OAuth配置错误:重定向URI无效,请检查后端配置");
|
|||
|
|
} else {
|
|||
|
|
const errorMessage = result.error.message || "未知错误";
|
|||
|
|
if (errorMessage.includes("already exists")) {
|
|||
|
|
toast.error("该邮箱已被注册,请直接登录");
|
|||
|
|
} else {
|
|||
|
|
toast.error(`创建失败: ${errorMessage}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("创建用户失败:", error);
|
|||
|
|
// 显示更详细的错误信息
|
|||
|
|
if (error.response) {
|
|||
|
|
console.error("响应状态:", error.response.status);
|
|||
|
|
console.error("响应数据:", error.response.data);
|
|||
|
|
}
|
|||
|
|
toast.error(`创建用户失败: ${error.message || "未知错误"}`);
|
|||
|
|
} finally {
|
|||
|
|
setIsLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="container mx-auto p-6 max-w-md">
|
|||
|
|
<Card>
|
|||
|
|
<CardHeader className="text-center">
|
|||
|
|
<CardTitle className="text-2xl">登录</CardTitle>
|
|||
|
|
<CardDescription>
|
|||
|
|
{isOIDCFlow ? (
|
|||
|
|
<span>OIDC Provider 授权登录</span>
|
|||
|
|
) : (
|
|||
|
|
<span>请登录您的账户</span>
|
|||
|
|
)}
|
|||
|
|
</CardDescription>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent>
|
|||
|
|
{isOIDCFlow && (
|
|||
|
|
<div className="mb-4 p-3 bg-blue-50 dark:bg-blue-950 rounded-md">
|
|||
|
|
<p className="text-sm text-blue-800 dark:text-blue-200">
|
|||
|
|
检测到 OIDC 授权流程。登录后将继续完成授权。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<form onSubmit={handleSignIn} className="space-y-4">
|
|||
|
|
<div className="space-y-2">
|
|||
|
|
<Label htmlFor="email">邮箱</Label>
|
|||
|
|
<Input
|
|||
|
|
id="email"
|
|||
|
|
type="email"
|
|||
|
|
value={credentials.email}
|
|||
|
|
onChange={(e) => setCredentials({ ...credentials, email: e.target.value })}
|
|||
|
|
disabled={isLoading}
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="space-y-2">
|
|||
|
|
<Label htmlFor="password">密码</Label>
|
|||
|
|
<Input
|
|||
|
|
id="password"
|
|||
|
|
type="password"
|
|||
|
|
value={credentials.password}
|
|||
|
|
onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
|
|||
|
|
disabled={isLoading}
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Button type="submit" className="w-full" disabled={isLoading}>
|
|||
|
|
{isLoading ? "登录中..." : "登录"}
|
|||
|
|
</Button>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<div className="mt-6 pt-4 border-t">
|
|||
|
|
<p className="text-sm text-muted-foreground mb-2">测试功能:</p>
|
|||
|
|
<div className="space-y-2">
|
|||
|
|
<Button
|
|||
|
|
onClick={handleCreateTestUser}
|
|||
|
|
variant="outline"
|
|||
|
|
className="w-full"
|
|||
|
|
disabled={isLoading}
|
|||
|
|
>
|
|||
|
|
创建测试用户
|
|||
|
|
</Button>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
{isOIDCFlow && (
|
|||
|
|
<div className="mt-4">
|
|||
|
|
<Button
|
|||
|
|
onClick={() => router.push('/test')}
|
|||
|
|
variant="ghost"
|
|||
|
|
className="w-full"
|
|||
|
|
>
|
|||
|
|
返回测试页面
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|