feat: add register page and logout button

This commit is contained in:
shinya
2025-07-02 23:34:07 +08:00
parent 2d066d5d26
commit 2027322a98
8 changed files with 229 additions and 17 deletions

View File

@@ -0,0 +1,49 @@
/* eslint-disable no-console */
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
export const runtime = 'edge';
// 读取存储类型环境变量,默认 localstorage
const STORAGE_TYPE =
(process.env.NEXT_PUBLIC_STORAGE_TYPE as string | undefined) ||
'localstorage';
export async function POST(req: NextRequest) {
try {
// localstorage 模式下不支持注册
if (STORAGE_TYPE === 'localstorage') {
return NextResponse.json(
{ error: '当前模式不支持注册' },
{ status: 400 }
);
}
const { username, password } = await req.json();
if (!username || typeof username !== 'string') {
return NextResponse.json({ error: '用户名不能为空' }, { status: 400 });
}
if (!password || typeof password !== 'string') {
return NextResponse.json({ error: '密码不能为空' }, { status: 400 });
}
try {
// 检查用户是否已存在
const exist = await db.checkUserExist(username);
if (exist) {
return NextResponse.json({ error: '用户已存在' }, { status: 400 });
}
await db.registerUser(username, password);
return NextResponse.json({ ok: true });
} catch (err) {
console.error('数据库注册失败', err);
return NextResponse.json({ error: '数据库错误' }, { status: 500 });
}
} catch (error) {
console.error('注册接口异常', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
}
}

View File

@@ -39,6 +39,7 @@ export default function RootLayout({
process.env.STORAGE_TYPE ||
process.env.NEXT_PUBLIC_STORAGE_TYPE ||
'localstorage',
ENABLE_REGISTER: process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true',
};
return (

View File

@@ -23,6 +23,11 @@ function LoginPageClient() {
(window as any).RUNTIME_CONFIG?.STORAGE_TYPE &&
(window as any).RUNTIME_CONFIG?.STORAGE_TYPE !== 'localstorage';
// 是否允许注册
const enableRegister =
typeof window !== 'undefined' &&
Boolean((window as any).RUNTIME_CONFIG?.ENABLE_REGISTER);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError(null);
@@ -62,6 +67,35 @@ function LoginPageClient() {
}
};
// 新增:处理注册逻辑
const handleRegister = async () => {
setError(null);
if (!password || !username) return;
try {
setLoading(true);
const res = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (res.ok) {
if (typeof window !== 'undefined') {
localStorage.setItem('password', password);
localStorage.setItem('username', username);
}
const redirect = searchParams.get('redirect') || '/';
router.replace(redirect);
} else {
const data = await res.json().catch(() => ({}));
setError(data.error ?? '服务器错误');
}
} finally {
setLoading(false);
}
};
return (
<div className='relative min-h-screen flex items-center justify-center px-4 overflow-hidden'>
<div className='absolute top-4 right-4'>
@@ -108,13 +142,38 @@ function LoginPageClient() {
<p className='text-sm text-red-600 dark:text-red-400'>{error}</p>
)}
<button
type='submit'
disabled={!password || loading || (shouldAskUsername && !username)}
className='inline-flex w-full justify-center rounded-lg bg-green-600 py-3 text-base font-semibold text-white shadow-lg transition-all duration-200 hover:from-green-600 hover:to-blue-600 disabled:cursor-not-allowed disabled:opacity-50'
>
{loading ? '登录中...' : '登录'}
</button>
{/* 登录 / 注册按钮 */}
{shouldAskUsername && enableRegister ? (
<div className='flex gap-4'>
<button
type='button'
onClick={handleRegister}
disabled={!password || !username || loading}
className='flex-1 inline-flex justify-center rounded-lg bg-blue-600 py-3 text-base font-semibold text-white shadow-lg transition-all duration-200 hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50'
>
{loading ? '注册中...' : '注册'}
</button>
<button
type='submit'
disabled={
!password || loading || (shouldAskUsername && !username)
}
className='flex-1 inline-flex justify-center rounded-lg bg-green-600 py-3 text-base font-semibold text-white shadow-lg transition-all duration-200 hover:from-green-600 hover:to-blue-600 disabled:cursor-not-allowed disabled:opacity-50'
>
{loading ? '登录中...' : '登录'}
</button>
</div>
) : (
<button
type='submit'
disabled={
!password || loading || (shouldAskUsername && !username)
}
className='inline-flex w-full justify-center rounded-lg bg-green-600 py-3 text-base font-semibold text-white shadow-lg transition-all duration-200 hover:from-green-600 hover:to-blue-600 disabled:cursor-not-allowed disabled:opacity-50'
>
{loading ? '登录中...' : '登录'}
</button>
)}
</form>
</div>
</div>