diff --git a/app/api/login/route.ts b/app/api/login/route.ts new file mode 100644 index 0000000..05fa9eb --- /dev/null +++ b/app/api/login/route.ts @@ -0,0 +1,23 @@ +// app/api/login/route.ts +import { NextResponse } from 'next/server'; +import { AUTH_USERNAME, AUTH_PASSWORD, COOKIE_NAME, COOKIE_MAX_AGE } from '@/lib/constants'; + +export async function POST(request: Request) { + const body = await request.json(); + const { username, password } = body; + + if (username === AUTH_USERNAME && password === AUTH_PASSWORD) { + const response = NextResponse.json({ success: true }); + response.cookies.set(COOKIE_NAME, 'ok', { + httpOnly: true, + maxAge: COOKIE_MAX_AGE, + path: '/', + }); + return response; + } + + return NextResponse.json( + { error: 'Invalid credentials' }, + { status: 401 } + ); +} diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 0000000..1a56ea2 --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,84 @@ +// app/login/page.tsx +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { COOKIE_NAME, COOKIE_MAX_AGE } from '@/lib/constants'; + +export default function LoginPage() { + const router = useRouter(); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + + // 这里应该调用 API 验证,简化起见直接比对 + // 实际项目中应该使用 API route 进行验证 + const response = await fetch('/api/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, password }), + }); + + if (response.ok) { + router.push('/download'); + } else { + setError('用户名或密码错误'); + } + }; + + return ( +
+
+

资源下载

+

登录

+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setUsername(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + required + /> +
+ +
+ + setPassword(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + required + /> +
+ + +
+
+
+ ); +}