new change
This commit is contained in:
50
src/app/api/auth/login/route.ts
Normal file
50
src/app/api/auth/login/route.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// 登录API端点
|
||||
import { login } from '@/lib/api/actions';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 验证请求方法
|
||||
if (request.method !== 'POST') {
|
||||
return NextResponse.json({ success: false, error: '不支持的请求方法' }, { status: 405 });
|
||||
}
|
||||
|
||||
// 验证请求体
|
||||
let credentials;
|
||||
try {
|
||||
credentials = await request.json();
|
||||
} catch (jsonError) {
|
||||
return NextResponse.json({ success: false, error: '请求体格式错误' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 基本参数验证
|
||||
if (!credentials || typeof credentials !== 'object') {
|
||||
return NextResponse.json({ success: false, error: '请求参数无效' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 调用登录函数
|
||||
const result = await login(credentials);
|
||||
|
||||
// 根据结果返回响应
|
||||
if (result.success) {
|
||||
// 设置CORS头
|
||||
const response = NextResponse.json({ success: true, user: result.user }, { status: 200 });
|
||||
response.headers.set('Access-Control-Allow-Origin', '*');
|
||||
response.headers.set('Access-Control-Allow-Methods', 'POST');
|
||||
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
return response;
|
||||
} else {
|
||||
// 根据不同错误类型返回不同的状态码
|
||||
const statusCode = result.error.includes('不存在') || result.error.includes('错误') ? 401 : 400;
|
||||
return NextResponse.json({ success: false, error: result.error }, { status: statusCode });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login API error:', error);
|
||||
// 对于不同类型的错误返回具体错误信息
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ success: false, error: error.message || '服务器内部错误' }, { status: 500 });
|
||||
}
|
||||
return NextResponse.json({ success: false, error: '服务器内部错误' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
69
src/app/api/auth/logout/route.ts
Normal file
69
src/app/api/auth/logout/route.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
// src/app/api/auth/logout/route.ts
|
||||
import { NextResponse } from 'next/server';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
// 退出登录API端点
|
||||
export async function POST() {
|
||||
try {
|
||||
// 获取cookie存储
|
||||
const cookieStore = cookies();
|
||||
|
||||
// 清除所有与next-auth相关的cookie
|
||||
const allCookies = cookieStore.getAll();
|
||||
allCookies.forEach(cookie => {
|
||||
if (cookie.name.includes('next-auth')) {
|
||||
cookieStore.delete(cookie.name);
|
||||
}
|
||||
});
|
||||
|
||||
// 返回成功响应
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '退出登录成功'
|
||||
}, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error('退出登录API错误:', error);
|
||||
// 对于不同类型的错误返回具体错误信息
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error.message || '服务器内部错误'
|
||||
}, { status: 500 });
|
||||
}
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '服务器内部错误'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// 处理GET请求,重定向到登录页面
|
||||
export async function GET() {
|
||||
try {
|
||||
// 创建重定向响应到登录页面
|
||||
const response = NextResponse.redirect(new URL('/login', process.env.NEXTAUTH_URL || 'http://localhost:3000'));
|
||||
|
||||
// 清除所有与next-auth相关的cookie
|
||||
const cookieStore = cookies();
|
||||
const allCookies = cookieStore.getAll();
|
||||
allCookies.forEach(cookie => {
|
||||
if (cookie.name.includes('next-auth')) {
|
||||
cookieStore.delete(cookie.name);
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('退出登录重定向错误:', error);
|
||||
// 发生错误时,仍然尝试重定向到登录页面
|
||||
try {
|
||||
return NextResponse.redirect(new URL('/login', process.env.NEXTAUTH_URL || 'http://localhost:3000'));
|
||||
} catch (redirectError) {
|
||||
// 如果重定向也失败,返回错误信息
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '退出登录失败,请刷新页面重试'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/app/api/auth/register/route.ts
Normal file
67
src/app/api/auth/register/route.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// 注册API端点
|
||||
import { register } from '@/lib/api/actions';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 验证请求方法
|
||||
if (request.method !== 'POST') {
|
||||
return NextResponse.json({ success: false, error: '不支持的请求方法' }, { status: 405 });
|
||||
}
|
||||
|
||||
// 验证请求体
|
||||
let userData;
|
||||
try {
|
||||
userData = await request.json();
|
||||
} catch (jsonError) {
|
||||
return NextResponse.json({ success: false, error: '请求体格式错误' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 基本参数验证
|
||||
if (!userData || typeof userData !== 'object') {
|
||||
return NextResponse.json({ success: false, error: '请求参数无效' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 调用注册函数
|
||||
const result = await register(userData);
|
||||
|
||||
// 根据结果返回响应
|
||||
if (result.success) {
|
||||
// 设置CORS头
|
||||
const response = NextResponse.json({
|
||||
success: true,
|
||||
message: '注册成功',
|
||||
user: result.user
|
||||
}, { status: 201 });
|
||||
response.headers.set('Access-Control-Allow-Origin', '*');
|
||||
response.headers.set('Access-Control-Allow-Methods', 'POST');
|
||||
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
return response;
|
||||
} else {
|
||||
// 根据不同错误类型返回不同的状态码
|
||||
let statusCode = 400;
|
||||
if (result.error.includes('已被注册')) {
|
||||
statusCode = 409; // 冲突状态码
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: result.error || '注册失败,请检查您的信息'
|
||||
}, { status: statusCode });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册API错误:', error);
|
||||
// 对于不同类型的错误返回具体错误信息
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error.message || '服务器内部错误'
|
||||
}, { status: 500 });
|
||||
}
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '服务器内部错误'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { register } from '@/lib/api/actions';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
|
||||
interface AuthFormProps {
|
||||
type: 'login' | 'register';
|
||||
@@ -17,6 +20,9 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
const [password, setPassword] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [minecraftUsername, setMinecraftUsername] = useState('');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -24,39 +30,108 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
|
||||
try {
|
||||
if (type === 'login') {
|
||||
// 使用next-auth的signIn功能进行登录
|
||||
// 基本验证
|
||||
if (!password) {
|
||||
setError('请输入密码');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const usernameField = username || email;
|
||||
if (!usernameField) {
|
||||
setError('请输入用户名或邮箱');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用next-auth的signIn功能进行登录,设置redirect为false以便处理错误
|
||||
const result = await signIn('credentials', {
|
||||
username: username || email, // 使用username或email作为用户名字段
|
||||
username: usernameField, // 使用username或email作为用户名字段
|
||||
email: email, // 传递email字段
|
||||
password,
|
||||
redirect: true, // 登录成功后自动重定向
|
||||
redirect: false, // 不自动重定向,以便处理错误
|
||||
callbackUrl: '/user-home' // 指定重定向目标
|
||||
});
|
||||
|
||||
console.log('登录结果:', result);
|
||||
if (result?.error) {
|
||||
// 处理登录错误 - 根据错误类型显示不同的错误信息
|
||||
console.error('登录失败:', result.error);
|
||||
if (result.error.includes('CredentialsSignin')) {
|
||||
setError('用户名或密码错误,请重试');
|
||||
} else if (result.error.includes('Network')) {
|
||||
setError('网络连接失败,请检查您的网络设置');
|
||||
} else {
|
||||
setError('登录失败,请稍后再试');
|
||||
}
|
||||
} else if (result?.ok) {
|
||||
// 登录成功,手动重定向
|
||||
window.location.href = '/user-home';
|
||||
} else {
|
||||
setError('登录过程中发生未知错误');
|
||||
}
|
||||
} else {
|
||||
// 注册逻辑可以在这里实现
|
||||
console.log('注册信息', { username, password, email, minecraftUsername });
|
||||
// 模拟注册请求
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
// 调用注册API
|
||||
const result = await register({
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
minecraftUsername
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
setSuccess('注册成功!即将跳转到登录页面...');
|
||||
// 注册成功后重定向到登录页面
|
||||
setTimeout(() => {
|
||||
window.location.href = '/login';
|
||||
}, 2000);
|
||||
} else {
|
||||
setError(result.error || '注册失败,请稍后再试');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('认证失败:', error);
|
||||
// 这里可以添加错误处理逻辑
|
||||
setError('网络错误,请检查您的连接');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 重置错误和成功消息
|
||||
const resetMessages = () => {
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
};
|
||||
|
||||
// 监听输入变化,清除错误消息
|
||||
const handleInputChange = (setter: React.Dispatch<React.SetStateAction<string>>) => {
|
||||
return (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
resetMessages();
|
||||
setter(e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{error && (
|
||||
<Alert variant="destructive" className="bg-red-50 border-red-200 text-red-800">
|
||||
<AlertTitle>错误</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<Alert className="bg-green-50 border-green-200 text-green-800">
|
||||
<AlertTitle>成功</AlertTitle>
|
||||
<AlertDescription>{success}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="username">用户名</Label>
|
||||
<Input
|
||||
id="username"
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
onChange={handleInputChange(setUsername)}
|
||||
placeholder={type === 'login' ? "用户名或邮箱" : "输入用户名"}
|
||||
/>
|
||||
</div>
|
||||
@@ -68,20 +143,29 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onChange={handleInputChange(setEmail)}
|
||||
placeholder="或直接输入邮箱登录"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="password">密码</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
required
|
||||
value={password}
|
||||
onChange={handleInputChange(setPassword)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 focus:outline-none"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{type === 'register' && (
|
||||
@@ -89,22 +173,23 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
<div>
|
||||
<Label htmlFor="email">电子邮箱</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={handleInputChange(setEmail)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="minecraftUsername">Minecraft 用户名</Label>
|
||||
<Input
|
||||
id="minecraftUsername"
|
||||
type="text"
|
||||
required
|
||||
value={minecraftUsername}
|
||||
onChange={(e) => setMinecraftUsername(e.target.value)}
|
||||
/>
|
||||
id="minecraftUsername"
|
||||
type="text"
|
||||
required
|
||||
value={minecraftUsername}
|
||||
onChange={handleInputChange(setMinecraftUsername)}
|
||||
placeholder="您的Minecraft游戏内用户名"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
51
src/components/ui/alert.tsx
Normal file
51
src/components/ui/alert.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
// src/components/ui/alert.tsx
|
||||
import React from 'react';
|
||||
|
||||
interface AlertProps {
|
||||
children: React.ReactNode;
|
||||
variant?: 'default' | 'destructive';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface AlertTitleProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface AlertDescriptionProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// 主Alert组件
|
||||
export function Alert({ children, variant = 'default', className = '' }: AlertProps) {
|
||||
const baseClasses = 'p-4 rounded-lg border flex items-start space-x-3';
|
||||
const variantClasses = {
|
||||
default: 'bg-green-50 border-green-200 text-green-800',
|
||||
destructive: 'bg-red-50 border-red-200 text-red-800'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${baseClasses} ${variantClasses[variant]} ${className}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Alert标题组件
|
||||
export function AlertTitle({ children, className = '' }: AlertTitleProps) {
|
||||
return (
|
||||
<h3 className={`font-medium text-sm ${className}`}>
|
||||
{children}
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
// Alert描述组件
|
||||
export function AlertDescription({ children, className = '' }: AlertDescriptionProps) {
|
||||
return (
|
||||
<p className={`text-sm ${className}`}>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user