From fbb68a46549ecf90acecd2bab38540f9c1c460de Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 18 Aug 2025 23:15:37 +0800 Subject: [PATCH] feat: disable register --- src/app/admin/page.tsx | 66 +----- src/app/api/admin/user/route.ts | 354 +++++++++++++++----------------- src/app/api/register/route.ts | 130 ------------ src/app/layout.tsx | 5 +- src/app/login/page.tsx | 72 +------ src/lib/admin.types.ts | 1 - src/lib/config.ts | 3 +- src/lib/downstream.ts | 3 - 8 files changed, 186 insertions(+), 448 deletions(-) delete mode 100644 src/app/api/register/route.ts diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 5816a48..d564984 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -135,9 +135,6 @@ interface UserConfigProps { } const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { - const [userSettings, setUserSettings] = useState({ - enableRegistration: false, - }); const [showAddUserForm, setShowAddUserForm] = useState(false); const [showChangePasswordForm, setShowChangePasswordForm] = useState(false); const [newUser, setNewUser] = useState({ @@ -152,41 +149,9 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { // 当前登录用户名 const currentUsername = getAuthInfoFromBrowserCookie()?.username || null; - useEffect(() => { - if (config?.UserConfig) { - setUserSettings({ - enableRegistration: config.UserConfig.AllowRegister, - }); - } - }, [config]); - // 切换允许注册设置 - const toggleAllowRegister = async (value: boolean) => { - try { - // 先更新本地 UI - setUserSettings((prev) => ({ ...prev, enableRegistration: value })); - const res = await fetch('/api/admin/user', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - action: 'setAllowRegister', - allowRegister: value, - }), - }); - if (!res.ok) { - const data = await res.json().catch(() => ({})); - throw new Error(data.error || `操作失败: ${res.status}`); - } - - await refreshConfig(); - } catch (err) { - showError(err instanceof Error ? err.message : '操作失败'); - // revert toggle UI - setUserSettings((prev) => ({ ...prev, enableRegistration: !value })); - } - }; const handleBanUser = async (uname: string) => { await handleUserAction('ban', uname); @@ -305,36 +270,7 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { - {/* 注册设置 */} -
-

- 注册设置 -

-
- - -
-
+ {/* 用户列表 */}
diff --git a/src/app/api/admin/user/route.ts b/src/app/api/admin/user/route.ts index a926c16..aaa012e 100644 --- a/src/app/api/admin/user/route.ts +++ b/src/app/api/admin/user/route.ts @@ -15,7 +15,6 @@ const ACTIONS = [ 'unban', 'setAdmin', 'cancelAdmin', - 'setAllowRegister', 'changePassword', 'deleteUser', ] as const; @@ -43,12 +42,10 @@ export async function POST(request: NextRequest) { const { targetUsername, // 目标用户名 targetPassword, // 目标用户密码(仅在添加用户时需要) - allowRegister, action, } = body as { targetUsername?: string; targetPassword?: string; - allowRegister?: boolean; action?: (typeof ACTIONS)[number]; }; @@ -56,12 +53,11 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '参数格式错误' }, { status: 400 }); } - if (action !== 'setAllowRegister' && !targetUsername) { + if (!targetUsername) { return NextResponse.json({ error: '缺少目标用户名' }, { status: 400 }); } if ( - action !== 'setAllowRegister' && action !== 'changePassword' && action !== 'deleteUser' && username === targetUsername @@ -105,188 +101,180 @@ export async function POST(request: NextRequest) { // 权限校验逻辑 const isTargetAdmin = targetEntry?.role === 'admin'; - if (action === 'setAllowRegister') { - if (typeof allowRegister !== 'boolean') { - return NextResponse.json({ error: '参数格式错误' }, { status: 400 }); - } - adminConfig.UserConfig.AllowRegister = allowRegister; - // 保存后直接返回成功(走后面的统一保存逻辑) - } else { - switch (action) { - case 'add': { - if (targetEntry) { - return NextResponse.json({ error: '用户已存在' }, { status: 400 }); - } - if (!targetPassword) { - return NextResponse.json( - { error: '缺少目标用户密码' }, - { status: 400 } - ); - } - await db.registerUser(targetUsername!, targetPassword); - // 更新配置 - adminConfig.UserConfig.Users.push({ - username: targetUsername!, - role: 'user', - }); - targetEntry = - adminConfig.UserConfig.Users[ - adminConfig.UserConfig.Users.length - 1 - ]; - break; + switch (action) { + case 'add': { + if (targetEntry) { + return NextResponse.json({ error: '用户已存在' }, { status: 400 }); } - case 'ban': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - if (isTargetAdmin) { - // 目标是管理员 - if (operatorRole !== 'owner') { - return NextResponse.json( - { error: '仅站长可封禁管理员' }, - { status: 401 } - ); - } - } - targetEntry.banned = true; - break; - } - case 'unban': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - if (isTargetAdmin) { - if (operatorRole !== 'owner') { - return NextResponse.json( - { error: '仅站长可操作管理员' }, - { status: 401 } - ); - } - } - targetEntry.banned = false; - break; - } - case 'setAdmin': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - if (targetEntry.role === 'admin') { - return NextResponse.json( - { error: '该用户已是管理员' }, - { status: 400 } - ); - } - if (operatorRole !== 'owner') { - return NextResponse.json( - { error: '仅站长可设置管理员' }, - { status: 401 } - ); - } - targetEntry.role = 'admin'; - break; - } - case 'cancelAdmin': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - if (targetEntry.role !== 'admin') { - return NextResponse.json( - { error: '目标用户不是管理员' }, - { status: 400 } - ); - } - if (operatorRole !== 'owner') { - return NextResponse.json( - { error: '仅站长可取消管理员' }, - { status: 401 } - ); - } - targetEntry.role = 'user'; - break; - } - case 'changePassword': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - if (!targetPassword) { - return NextResponse.json({ error: '缺少新密码' }, { status: 400 }); - } - - // 权限检查:不允许修改站长密码 - if (targetEntry.role === 'owner') { - return NextResponse.json( - { error: '无法修改站长密码' }, - { status: 401 } - ); - } - - if ( - isTargetAdmin && - operatorRole !== 'owner' && - username !== targetUsername - ) { - return NextResponse.json( - { error: '仅站长可修改其他管理员密码' }, - { status: 401 } - ); - } - - await db.changePassword(targetUsername!, targetPassword); - break; - } - case 'deleteUser': { - if (!targetEntry) { - return NextResponse.json( - { error: '目标用户不存在' }, - { status: 404 } - ); - } - - // 权限检查:站长可删除所有用户(除了自己),管理员可删除普通用户 - if (username === targetUsername) { - return NextResponse.json( - { error: '不能删除自己' }, - { status: 400 } - ); - } - - if (isTargetAdmin && operatorRole !== 'owner') { - return NextResponse.json( - { error: '仅站长可删除管理员' }, - { status: 401 } - ); - } - - await db.deleteUser(targetUsername!); - - // 从配置中移除用户 - const userIndex = adminConfig.UserConfig.Users.findIndex( - (u) => u.username === targetUsername + if (!targetPassword) { + return NextResponse.json( + { error: '缺少目标用户密码' }, + { status: 400 } ); - if (userIndex > -1) { - adminConfig.UserConfig.Users.splice(userIndex, 1); - } - - break; } - default: - return NextResponse.json({ error: '未知操作' }, { status: 400 }); + await db.registerUser(targetUsername!, targetPassword); + // 更新配置 + adminConfig.UserConfig.Users.push({ + username: targetUsername!, + role: 'user', + }); + targetEntry = + adminConfig.UserConfig.Users[ + adminConfig.UserConfig.Users.length - 1 + ]; + break; } + case 'ban': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + if (isTargetAdmin) { + // 目标是管理员 + if (operatorRole !== 'owner') { + return NextResponse.json( + { error: '仅站长可封禁管理员' }, + { status: 401 } + ); + } + } + targetEntry.banned = true; + break; + } + case 'unban': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + if (isTargetAdmin) { + if (operatorRole !== 'owner') { + return NextResponse.json( + { error: '仅站长可操作管理员' }, + { status: 401 } + ); + } + } + targetEntry.banned = false; + break; + } + case 'setAdmin': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + if (targetEntry.role === 'admin') { + return NextResponse.json( + { error: '该用户已是管理员' }, + { status: 400 } + ); + } + if (operatorRole !== 'owner') { + return NextResponse.json( + { error: '仅站长可设置管理员' }, + { status: 401 } + ); + } + targetEntry.role = 'admin'; + break; + } + case 'cancelAdmin': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + if (targetEntry.role !== 'admin') { + return NextResponse.json( + { error: '目标用户不是管理员' }, + { status: 400 } + ); + } + if (operatorRole !== 'owner') { + return NextResponse.json( + { error: '仅站长可取消管理员' }, + { status: 401 } + ); + } + targetEntry.role = 'user'; + break; + } + case 'changePassword': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + if (!targetPassword) { + return NextResponse.json({ error: '缺少新密码' }, { status: 400 }); + } + + // 权限检查:不允许修改站长密码 + if (targetEntry.role === 'owner') { + return NextResponse.json( + { error: '无法修改站长密码' }, + { status: 401 } + ); + } + + if ( + isTargetAdmin && + operatorRole !== 'owner' && + username !== targetUsername + ) { + return NextResponse.json( + { error: '仅站长可修改其他管理员密码' }, + { status: 401 } + ); + } + + await db.changePassword(targetUsername!, targetPassword); + break; + } + case 'deleteUser': { + if (!targetEntry) { + return NextResponse.json( + { error: '目标用户不存在' }, + { status: 404 } + ); + } + + // 权限检查:站长可删除所有用户(除了自己),管理员可删除普通用户 + if (username === targetUsername) { + return NextResponse.json( + { error: '不能删除自己' }, + { status: 400 } + ); + } + + if (isTargetAdmin && operatorRole !== 'owner') { + return NextResponse.json( + { error: '仅站长可删除管理员' }, + { status: 401 } + ); + } + + await db.deleteUser(targetUsername!); + + // 从配置中移除用户 + const userIndex = adminConfig.UserConfig.Users.findIndex( + (u) => u.username === targetUsername + ); + if (userIndex > -1) { + adminConfig.UserConfig.Users.splice(userIndex, 1); + } + + break; + } + default: + return NextResponse.json({ error: '未知操作' }, { status: 400 }); } // 将更新后的配置写入数据库 diff --git a/src/app/api/register/route.ts b/src/app/api/register/route.ts deleted file mode 100644 index 79ef281..0000000 --- a/src/app/api/register/route.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* 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 = 'nodejs'; - -// 读取存储类型环境变量,默认 localstorage -const STORAGE_TYPE = - (process.env.NEXT_PUBLIC_STORAGE_TYPE as - | 'localstorage' - | 'redis' - | 'upstash' - | 'kvrocks' - | undefined) || 'localstorage'; - -// 生成签名 -async function generateSignature( - data: string, - secret: string -): Promise { - 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 { - const authData: any = { - role: 'user', - username, - timestamp: Date.now(), - }; - - // 使用process.env.PASSWORD作为签名密钥,而不是用户密码 - const signingKey = process.env.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 }); - } -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e56f085..ace6098 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -46,7 +46,7 @@ export default async function RootLayout({ let announcement = process.env.ANNOUNCEMENT || '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。'; - let enableRegister = process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true'; + let doubanProxyType = process.env.NEXT_PUBLIC_DOUBAN_PROXY_TYPE || 'melody-cdn-sharon'; let doubanProxy = process.env.NEXT_PUBLIC_DOUBAN_PROXY || ''; let doubanImageProxyType = @@ -64,7 +64,7 @@ export default async function RootLayout({ const config = await getConfig(); siteName = config.SiteConfig.SiteName; announcement = config.SiteConfig.Announcement; - enableRegister = config.UserConfig.AllowRegister; + doubanProxyType = config.SiteConfig.DoubanProxyType; doubanProxy = config.SiteConfig.DoubanProxy; doubanImageProxyType = config.SiteConfig.DoubanImageProxyType; @@ -83,7 +83,6 @@ export default async function RootLayout({ // 将运行时配置注入到全局 window 对象,供客户端在运行时读取 const runtimeConfig = { STORAGE_TYPE: process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage', - ENABLE_REGISTER: enableRegister, DOUBAN_PROXY_TYPE: doubanProxyType, DOUBAN_PROXY: doubanProxy, DOUBAN_IMAGE_PROXY_TYPE: doubanImageProxyType, diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 85ddf5d..a0f2e72 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -75,7 +75,7 @@ function LoginPageClient() { const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [shouldAskUsername, setShouldAskUsername] = useState(false); - const [enableRegister, setEnableRegister] = useState(false); + const { siteName } = useSite(); // 在客户端挂载后设置配置 @@ -83,9 +83,6 @@ function LoginPageClient() { if (typeof window !== 'undefined') { const storageType = (window as any).RUNTIME_CONFIG?.STORAGE_TYPE; setShouldAskUsername(storageType && storageType !== 'localstorage'); - setEnableRegister( - Boolean((window as any).RUNTIME_CONFIG?.ENABLE_REGISTER) - ); } }, []); @@ -122,32 +119,7 @@ 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) { - const redirect = searchParams.get('redirect') || '/'; - router.replace(redirect); - } else { - const data = await res.json().catch(() => ({})); - setError(data.error ?? '服务器错误'); - } - } catch (error) { - setError('网络错误,请稍后重试'); - } finally { - setLoading(false); - } - }; return (
@@ -195,38 +167,16 @@ function LoginPageClient() {

{error}

)} - {/* 登录 / 注册按钮 */} - {shouldAskUsername && enableRegister ? ( -
- - -
- ) : ( - - )} + {/* 登录按钮 */} +
diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts index 8cddf28..0179294 100644 --- a/src/lib/admin.types.ts +++ b/src/lib/admin.types.ts @@ -18,7 +18,6 @@ export interface AdminConfig { FluidSearch: boolean; }; UserConfig: { - AllowRegister: boolean; Users: { username: string; role: 'user' | 'admin' | 'owner'; diff --git a/src/lib/config.ts b/src/lib/config.ts index 521981b..be70cb6 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -172,7 +172,6 @@ async function getInitConfig(configFile: string, subConfig: { process.env.NEXT_PUBLIC_FLUID_SEARCH !== 'false', }, UserConfig: { - AllowRegister: process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true', Users: [], }, SourceConfig: [], @@ -251,7 +250,7 @@ export async function getConfig(): Promise { export function configSelfCheck(adminConfig: AdminConfig): AdminConfig { // 确保必要的属性存在和初始化 if (!adminConfig.UserConfig) { - adminConfig.UserConfig = { AllowRegister: false, Users: [] }; + adminConfig.UserConfig = { Users: [] }; } if (!adminConfig.UserConfig.Users || !Array.isArray(adminConfig.UserConfig.Users)) { adminConfig.UserConfig.Users = []; diff --git a/src/lib/downstream.ts b/src/lib/downstream.ts index 6cef046..25f0e4a 100644 --- a/src/lib/downstream.ts +++ b/src/lib/downstream.ts @@ -58,9 +58,6 @@ async function searchWithCache( } const data = await response.json(); - if (apiSite.key === 'xiaomaomi') { - console.log(data); - } if ( !data || !data.list ||