casualroom/apps/fenghuo/web/app/[locale]/sign-in/page.tsx

184 lines
7.3 KiB
TypeScript
Raw Normal View History

2025-07-28 07:50:50 +08:00
"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>
);
}