131 lines
3.7 KiB
TypeScript
131 lines
3.7 KiB
TypeScript
/* eslint-disable no-console,@typescript-eslint/no-explicit-any */
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
|
|
import { getConfig } from '@/lib/config';
|
|
import { db } from '@/lib/db';
|
|
|
|
export const runtime = 'edge';
|
|
|
|
// 读取存储类型环境变量,默认 localstorage
|
|
const STORAGE_TYPE =
|
|
(process.env.NEXT_PUBLIC_STORAGE_TYPE as
|
|
| 'localstorage'
|
|
| 'redis'
|
|
| 'd1'
|
|
| 'upstash'
|
|
| undefined) || 'localstorage';
|
|
|
|
// 生成签名
|
|
async function generateSignature(
|
|
data: string,
|
|
secret: string
|
|
): Promise<string> {
|
|
const encoder = new TextEncoder();
|
|
const keyData = encoder.encode(secret);
|
|
const messageData = encoder.encode(data);
|
|
|
|
// 导入密钥
|
|
const key = await crypto.subtle.importKey(
|
|
'raw',
|
|
keyData,
|
|
{ name: 'HMAC', hash: 'SHA-256' },
|
|
false,
|
|
['sign']
|
|
);
|
|
|
|
// 生成签名
|
|
const signature = await crypto.subtle.sign('HMAC', key, messageData);
|
|
|
|
// 转换为十六进制字符串
|
|
return Array.from(new Uint8Array(signature))
|
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
}
|
|
|
|
// 生成认证Cookie(带签名)
|
|
async function generateAuthCookie(username: string): Promise<string> {
|
|
const authData: any = {
|
|
role: 'user',
|
|
username,
|
|
timestamp: Date.now(),
|
|
};
|
|
|
|
// 使用process.env.AUTH_PASSWORD作为签名密钥,而不是用户密码
|
|
const signingKey = process.env.AUTH_PASSWORD || '';
|
|
const signature = await generateSignature(username, signingKey);
|
|
authData.signature = signature;
|
|
|
|
return encodeURIComponent(JSON.stringify(authData));
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
// localstorage 模式下不支持注册
|
|
if (STORAGE_TYPE === 'localstorage') {
|
|
return NextResponse.json(
|
|
{ error: '当前模式不支持注册' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const config = await getConfig();
|
|
// 校验是否开放注册
|
|
if (!config.UserConfig.AllowRegister) {
|
|
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 });
|
|
}
|
|
|
|
// 检查是否和管理员重复
|
|
if (username === process.env.USERNAME) {
|
|
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);
|
|
|
|
// 添加到配置中并保存
|
|
config.UserConfig.Users.push({
|
|
username,
|
|
role: 'user',
|
|
});
|
|
await db.saveAdminConfig(config);
|
|
|
|
// 注册成功,设置认证cookie
|
|
const response = NextResponse.json({ ok: true });
|
|
const cookieValue = await generateAuthCookie(username);
|
|
const expires = new Date();
|
|
expires.setDate(expires.getDate() + 7); // 7天过期
|
|
|
|
response.cookies.set('auth', cookieValue, {
|
|
path: '/',
|
|
expires,
|
|
sameSite: 'lax', // 改为 lax 以支持 PWA
|
|
httpOnly: false, // PWA 需要客户端可访问
|
|
secure: false, // 根据协议自动设置
|
|
});
|
|
|
|
return response;
|
|
} catch (err) {
|
|
console.error('数据库注册失败', err);
|
|
return NextResponse.json({ error: '数据库错误' }, { status: 500 });
|
|
}
|
|
} catch (error) {
|
|
console.error('注册接口异常', error);
|
|
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
|
|
}
|
|
}
|