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

279 lines
13 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";
2025-07-28 10:32:25 +08:00
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@nice/ui/components/tabs";
2025-07-28 07:50:50 +08:00
import { toast } from "@nice/ui/components/sonner";
import { useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
export default function SignInPage() {
2025-07-28 10:32:25 +08:00
const [loginCredentials, setLoginCredentials] = useState({
email: "",
username: "",
password: ""
});
const [registerCredentials, setRegisterCredentials] = useState({
email: "",
username: "",
password: "",
confirmPassword: "",
name: ""
2025-07-28 07:50:50 +08:00
});
const [isLoading, setIsLoading] = useState(false);
2025-07-28 10:32:25 +08:00
const [currentTab, setCurrentTab] = useState("login");
2025-07-28 07:50:50 +08:00
const router = useRouter();
const searchParams = useSearchParams();
// 检查是否是从 OIDC Provider 重定向过来的
const isOIDCFlow = searchParams.get('response_type') === 'code' || searchParams.get('client_id');
2025-07-28 10:32:25 +08:00
// 登录处理
2025-07-28 07:50:50 +08:00
const handleSignIn = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
try {
2025-07-28 10:32:25 +08:00
const result = await client.signIn.username({
username: loginCredentials.username || loginCredentials.email,
password: loginCredentials.password,
callbackURL: 'http://localhost:3000/dashboard'
2025-07-28 07:50:50 +08:00
});
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);
}
};
2025-07-28 10:32:25 +08:00
// 注册处理
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault();
if (registerCredentials.password !== registerCredentials.confirmPassword) {
toast.error("密码不匹配");
return;
}
2025-07-28 07:50:50 +08:00
setIsLoading(true);
2025-07-28 10:32:25 +08:00
2025-07-28 07:50:50 +08:00
try {
const result = await client.signUp.email({
2025-07-28 10:32:25 +08:00
email: registerCredentials.email,
password: registerCredentials.password,
name: registerCredentials.name,
username: registerCredentials.username,
callbackURL: 'http://localhost:3000/dashboard'
2025-07-28 07:50:50 +08:00
});
if (result.data) {
2025-07-28 10:32:25 +08:00
toast.success("注册成功!");
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
// 如果是 OIDC 流程,重定向回原始请求
if (isOIDCFlow) {
const params = new URLSearchParams(window.location.search);
const redirectUrl = `http://localhost:3001/api/auth/oauth2/authorize?${params.toString()}`;
window.location.href = redirectUrl;
2025-07-28 07:50:50 +08:00
} else {
2025-07-28 10:32:25 +08:00
router.push('/dashboard');
2025-07-28 07:50:50 +08:00
}
2025-07-28 10:32:25 +08:00
} else if (result.error) {
toast.error(`注册失败: ${result.error.message}`);
2025-07-28 07:50:50 +08:00
}
} catch (error: any) {
2025-07-28 10:32:25 +08:00
console.error("注册失败:", error);
toast.error(`注册失败: ${error.message || "未知错误"}`);
2025-07-28 07:50:50 +08:00
} finally {
setIsLoading(false);
}
};
2025-07-28 10:32:25 +08:00
2025-07-28 07:50:50 +08:00
return (
<div className="container mx-auto p-6 max-w-md">
2025-07-28 10:32:25 +08:00
<div className="mb-4 text-center">
<Button
onClick={() => router.push('/test')}
variant="ghost"
size="sm"
>
OIDC
</Button>
</div>
2025-07-28 07:50:50 +08:00
<Card>
<CardHeader className="text-center">
2025-07-28 10:32:25 +08:00
<CardTitle className="text-2xl">
{isOIDCFlow ? "授权登录" : "用户认证"}
</CardTitle>
2025-07-28 07:50:50 +08:00
<CardDescription>
{isOIDCFlow ? (
<span>OIDC Provider </span>
) : (
2025-07-28 10:32:25 +08:00
<span></span>
2025-07-28 07:50:50 +08:00
)}
</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">
2025-07-28 10:32:25 +08:00
OIDC
2025-07-28 07:50:50 +08:00
</p>
</div>
)}
2025-07-28 10:32:25 +08:00
<Tabs value={currentTab} onValueChange={setCurrentTab}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="login"></TabsTrigger>
<TabsTrigger value="register"></TabsTrigger>
</TabsList>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
{/* 登录表单 */}
<TabsContent value="login" className="space-y-4 mt-4">
<form onSubmit={handleSignIn} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="login-email"></Label>
<Input
id="login-email"
type="text"
placeholder="输入邮箱或用户名"
value={loginCredentials.email}
onChange={(e) => setLoginCredentials({ ...loginCredentials, email: e.target.value })}
disabled={isLoading}
required
/>
</div>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
<div className="space-y-2">
<Label htmlFor="login-password"></Label>
<Input
id="login-password"
type="password"
placeholder="输入密码"
value={loginCredentials.password}
onChange={(e) => setLoginCredentials({ ...loginCredentials, password: e.target.value })}
disabled={isLoading}
required
/>
</div>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? "登录中..." : "登录"}
</Button>
</form>
</TabsContent>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
{/* 注册表单 */}
<TabsContent value="register" className="space-y-4 mt-4">
<form onSubmit={handleSignUp} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="register-name"></Label>
<Input
id="register-name"
type="text"
placeholder="输入您的姓名"
value={registerCredentials.name}
onChange={(e) => setRegisterCredentials({ ...registerCredentials, name: e.target.value })}
disabled={isLoading}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-username"></Label>
<Input
id="register-username"
type="text"
placeholder="输入用户名"
value={registerCredentials.username}
onChange={(e) => setRegisterCredentials({ ...registerCredentials, username: e.target.value })}
disabled={isLoading}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-email"></Label>
<Input
id="register-email"
type="email"
placeholder="输入邮箱地址"
value={registerCredentials.email}
onChange={(e) => setRegisterCredentials({ ...registerCredentials, email: e.target.value })}
disabled={isLoading}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-password"></Label>
<Input
id="register-password"
type="password"
placeholder="输入密码"
value={registerCredentials.password}
onChange={(e) => setRegisterCredentials({ ...registerCredentials, password: e.target.value })}
disabled={isLoading}
required
/>
</div>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
<div className="space-y-2">
<Label htmlFor="register-confirm-password"></Label>
<Input
id="register-confirm-password"
type="password"
placeholder="再次输入密码"
value={registerCredentials.confirmPassword}
onChange={(e) => setRegisterCredentials({ ...registerCredentials, confirmPassword: e.target.value })}
disabled={isLoading}
required
/>
</div>
2025-07-28 07:50:50 +08:00
2025-07-28 10:32:25 +08:00
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? "注册中..." : "注册"}
</Button>
</form>
</TabsContent>
</Tabs>
2025-07-28 07:50:50 +08:00
{isOIDCFlow && (
<div className="mt-4">
<Button
onClick={() => router.push('/test')}
variant="ghost"
className="w-full"
>
</Button>
</div>
)}
</CardContent>
</Card>
</div>
);
}