From ca9a7403eb446e00dee501eab96f49c6c94b96e0 Mon Sep 17 00:00:00 2001 From: shinya Date: Fri, 22 Aug 2025 22:44:15 +0800 Subject: [PATCH] feat: add batch source and user operates --- src/app/admin/page.tsx | 454 ++++++++++++++++++++++-- src/app/api/admin/source/route.ts | 86 ++++- src/app/api/admin/user/route.ts | 42 ++- src/app/api/favorites/route.ts | 27 +- src/app/api/playrecords/route.ts | 27 +- src/app/api/search/suggestions/route.ts | 10 - src/app/api/searchhistory/route.ts | 27 +- src/app/api/skipconfigs/route.ts | 27 +- 8 files changed, 626 insertions(+), 74 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index eca1ad9..11df295 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, no-console, @typescript-eslint/no-non-null-assertion,react-hooks/exhaustive-deps */ +/* eslint-disable @typescript-eslint/no-explicit-any, no-console, @typescript-eslint/no-non-null-assertion,react-hooks/exhaustive-deps,@typescript-eslint/no-empty-function */ 'use client'; @@ -37,7 +37,7 @@ import { Video, } from 'lucide-react'; import { GripVertical } from 'lucide-react'; -import { Suspense, useCallback, useEffect, useState } from 'react'; +import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import { AdminConfig, AdminConfigResult } from '@/lib/admin.types'; @@ -301,6 +301,9 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { tags?: string[]; } | null>(null); const [selectedUserGroups, setSelectedUserGroups] = useState([]); + const [selectedUsers, setSelectedUsers] = useState>(new Set()); + const [showBatchUserGroupModal, setShowBatchUserGroupModal] = useState(false); + const [selectedUserGroup, setSelectedUserGroup] = useState(''); const [showDeleteUserGroupModal, setShowDeleteUserGroupModal] = useState(false); const [deletingUserGroup, setDeletingUserGroup] = useState<{ name: string; @@ -312,6 +315,17 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { // 当前登录用户名 const currentUsername = getAuthInfoFromBrowserCookie()?.username || null; + // 使用 useMemo 计算全选状态,避免每次渲染都重新计算 + const selectAllUsers = useMemo(() => { + const selectableUserCount = config?.UserConfig?.Users?.filter(user => + (role === 'owner' || + (role === 'admin' && + (user.role === 'user' || + user.username === currentUsername))) + ).length || 0; + return selectedUsers.size === selectableUserCount && selectedUsers.size > 0; + }, [selectedUsers.size, config?.UserConfig?.Users, role, currentUsername]); + // 获取用户组列表 const userGroups = config?.UserConfig?.Tags || []; @@ -498,6 +512,69 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { } }; + // 处理用户选择 + const handleSelectUser = useCallback((username: string, checked: boolean) => { + setSelectedUsers(prev => { + const newSelectedUsers = new Set(prev); + if (checked) { + newSelectedUsers.add(username); + } else { + newSelectedUsers.delete(username); + } + return newSelectedUsers; + }); + }, []); + + const handleSelectAllUsers = useCallback((checked: boolean) => { + if (checked) { + // 只选择自己有权限操作的用户 + const selectableUsernames = config?.UserConfig?.Users?.filter(user => + (role === 'owner' || + (role === 'admin' && + (user.role === 'user' || + user.username === currentUsername))) + ).map(u => u.username) || []; + setSelectedUsers(new Set(selectableUsernames)); + } else { + setSelectedUsers(new Set()); + } + }, [config?.UserConfig?.Users, role, currentUsername]); + + // 批量设置用户组 + const handleBatchSetUserGroup = async (userGroup: string) => { + if (selectedUsers.size === 0) return; + + try { + const res = await fetch('/api/admin/user', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'batchUpdateUserGroups', + usernames: Array.from(selectedUsers), + userGroups: userGroup === '' ? [] : [userGroup], + }), + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || `操作失败: ${res.status}`); + } + + const userCount = selectedUsers.size; + setSelectedUsers(new Set()); + setShowBatchUserGroupModal(false); + setSelectedUserGroup(''); + showSuccess(`已为 ${userCount} 个用户设置用户组: ${userGroup}`, showAlert); + + // 刷新配置 + await refreshConfig(); + } catch (err) { + showError('批量设置用户组失败', showAlert); + } + }; + + + // 提取URL域名的辅助函数 const extractDomain = (url: string): string => { try { @@ -636,9 +713,9 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { {/* 用户组列表 */} -
+
- +
用户组名称 @@ -700,18 +777,37 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => {

用户列表

- +
+ {/* 批量操作按钮 */} + {selectedUsers.size > 0 && ( + <> +
+ + 已选择 {selectedUsers.size} 个用户 + + +
+
+ + )} + +
{/* 添加用户表单 */} @@ -817,10 +913,33 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { )} {/* 用户列表 */} -
+
- + + @@ -1669,6 +1804,92 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { document.body )} + {/* 批量设置用户组弹窗 */} + {showBatchUserGroupModal && createPortal( +
{ + setShowBatchUserGroupModal(false); + setSelectedUserGroup(''); + }}> +
e.stopPropagation()}> +
+
+

+ 批量设置用户组 +

+ +
+ +
+
+
+ + + + + 批量操作说明 + +
+

+ 将为选中的 {selectedUsers.size} 个用户 设置用户组,选择"无用户组"为无限制 +

+
+ +
+ + +

+ 选择"无用户组"为无限制,选择特定用户组将限制用户只能访问该用户组允许的采集源 +

+
+
+ + {/* 操作按钮 */} +
+ + +
+
+
+
, + document.body + )} + {/* 通用弹窗组件 */} >(new Set()); + + // 使用 useMemo 计算全选状态,避免每次渲染都重新计算 + const selectAll = useMemo(() => { + return selectedSources.size === sources.length && selectedSources.size > 0; + }, [selectedSources.size, sources.length]); + + // 确认弹窗状态 + const [confirmModal, setConfirmModal] = useState<{ + isOpen: boolean; + title: string; + message: string; + onConfirm: () => void; + onCancel: () => void; + }>({ + isOpen: false, + title: '', + message: '', + onConfirm: () => { }, + onCancel: () => { } + }); + // 有效性检测相关状态 const [showValidationModal, setShowValidationModal] = useState(false); const [searchKeyword, setSearchKeyword] = useState(''); @@ -1739,6 +1983,8 @@ const VideoSourceConfig = ({ setSources(config.SourceConfig); // 进入时重置 orderChanged setOrderChanged(false); + // 重置选择状态 + setSelectedSources(new Set()); } }, [config]); @@ -1981,6 +2227,14 @@ const VideoSourceConfig = ({ > + @@ -2049,6 +2303,77 @@ const VideoSourceConfig = ({ ); }; + // 全选/取消全选 + const handleSelectAll = useCallback((checked: boolean) => { + if (checked) { + const allKeys = sources.map(s => s.key); + setSelectedSources(new Set(allKeys)); + } else { + setSelectedSources(new Set()); + } + }, [sources]); + + // 单个选择 + const handleSelectSource = useCallback((key: string, checked: boolean) => { + setSelectedSources(prev => { + const newSelected = new Set(prev); + if (checked) { + newSelected.add(key); + } else { + newSelected.delete(key); + } + return newSelected; + }); + }, []); + + // 批量操作 + const handleBatchOperation = async (action: 'batch_enable' | 'batch_disable' | 'batch_delete') => { + if (selectedSources.size === 0) { + showAlert({ type: 'warning', title: '请先选择要操作的视频源', message: '请选择至少一个视频源' }); + return; + } + + const keys = Array.from(selectedSources); + let confirmMessage = ''; + let actionName = ''; + + switch (action) { + case 'batch_enable': + confirmMessage = `确定要启用选中的 ${keys.length} 个视频源吗?`; + actionName = '批量启用'; + break; + case 'batch_disable': + confirmMessage = `确定要禁用选中的 ${keys.length} 个视频源吗?`; + actionName = '批量禁用'; + break; + case 'batch_delete': + confirmMessage = `确定要删除选中的 ${keys.length} 个视频源吗?此操作不可恢复!`; + actionName = '批量删除'; + break; + } + + // 显示确认弹窗 + setConfirmModal({ + isOpen: true, + title: '确认操作', + message: confirmMessage, + onConfirm: async () => { + try { + await callSourceApi({ action, keys }); + showAlert({ type: 'success', title: `${actionName}成功`, message: `${actionName}了 ${keys.length} 个视频源`, timer: 2000 }); + // 重置选择状态 + setSelectedSources(new Set()); + } catch (err) { + showAlert({ type: 'error', title: `${actionName}失败`, message: err instanceof Error ? err.message : '操作失败' }); + } + setConfirmModal({ isOpen: false, title: '', message: '', onConfirm: () => { }, onCancel: () => { } }); + }, + onCancel: () => { + setConfirmModal({ isOpen: false, title: '', message: '', onConfirm: () => { }, onCancel: () => { } }); + } + }); + }; + if (!config) { return (
@@ -2065,6 +2390,35 @@ const VideoSourceConfig = ({ 视频源列表
+ {/* 批量操作按钮 */} + {selectedSources.size > 0 && ( + <> +
+ + 已选择 {selectedSources.size} 个视频源 + + + + +
+
+ + )}
)} + + {/* 视频源表格 */} -
+
+ + {(() => { + // 检查是否有权限操作任何用户 + const hasAnyPermission = config?.UserConfig?.Users?.some(user => + (role === 'owner' || + (role === 'admin' && + (user.role === 'user' || + user.username === currentUsername))) + ); + + return hasAnyPermission ? ( + handleSelectAllUsers(e.target.checked)} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600' + /> + ) : ( +
+ ); + })()} +
{ key={user.username} className='hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors' > + + + {(role === 'owner' || + (role === 'admin' && + (user.role === 'user' || + user.username === currentUsername))) ? ( + handleSelectUser(user.username, e.target.checked)} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600' + /> + ) : ( +
+ )} +
{user.username} + handleSelectSource(source.key, e.target.checked)} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600' + /> + {source.name}
- + @@ -2255,6 +2619,52 @@ const VideoSourceConfig = ({ timer={alertModal.timer} showConfirm={alertModal.showConfirm} /> + + {/* 批量操作确认弹窗 */} + {confirmModal.isOpen && createPortal( +
+
e.stopPropagation()}> +
+
+

+ {confirmModal.title} +

+ +
+ +
+

+ {confirmModal.message} +

+
+ + {/* 操作按钮 */} +
+ + +
+
+
+
, + document.body + )} ); }; @@ -2535,9 +2945,9 @@ const CategoryConfig = ({ )} {/* 分类表格 */} -
+
+ + handleSelectAll(e.target.checked)} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600' + /> + 名称
- +
diff --git a/src/app/api/admin/source/route.ts b/src/app/api/admin/source/route.ts index 9f0bedb..dafacfb 100644 --- a/src/app/api/admin/source/route.ts +++ b/src/app/api/admin/source/route.ts @@ -9,7 +9,7 @@ import { db } from '@/lib/db'; export const runtime = 'nodejs'; // 支持的操作类型 -type Action = 'add' | 'disable' | 'enable' | 'delete' | 'sort'; +type Action = 'add' | 'disable' | 'enable' | 'delete' | 'sort' | 'batch_disable' | 'batch_enable' | 'batch_delete'; interface BaseBody { action?: Action; @@ -37,7 +37,7 @@ export async function POST(request: NextRequest) { const username = authInfo.username; // 基础校验 - const ACTIONS: Action[] = ['add', 'disable', 'enable', 'delete', 'sort']; + const ACTIONS: Action[] = ['add', 'disable', 'enable', 'delete', 'sort', 'batch_disable', 'batch_enable', 'batch_delete']; if (!username || !action || !ACTIONS.includes(action)) { return NextResponse.json({ error: '参数格式错误' }, { status: 400 }); } @@ -111,6 +111,88 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '该源不可删除' }, { status: 400 }); } adminConfig.SourceConfig.splice(idx, 1); + + // 检查并清理用户组和用户的权限数组 + // 清理用户组权限 + if (adminConfig.UserConfig.Tags) { + adminConfig.UserConfig.Tags.forEach(tag => { + if (tag.enabledApis) { + tag.enabledApis = tag.enabledApis.filter(api => api !== key); + } + }); + } + + // 清理用户权限 + adminConfig.UserConfig.Users.forEach(user => { + if (user.enabledApis) { + user.enabledApis = user.enabledApis.filter(api => api !== key); + } + }); + break; + } + case 'batch_disable': { + const { keys } = body as { keys?: string[] }; + if (!Array.isArray(keys) || keys.length === 0) { + return NextResponse.json({ error: '缺少 keys 参数或为空' }, { status: 400 }); + } + keys.forEach(key => { + const entry = adminConfig.SourceConfig.find((s) => s.key === key); + if (entry) { + entry.disabled = true; + } + }); + break; + } + case 'batch_enable': { + const { keys } = body as { keys?: string[] }; + if (!Array.isArray(keys) || keys.length === 0) { + return NextResponse.json({ error: '缺少 keys 参数或为空' }, { status: 400 }); + } + keys.forEach(key => { + const entry = adminConfig.SourceConfig.find((s) => s.key === key); + if (entry) { + entry.disabled = false; + } + }); + break; + } + case 'batch_delete': { + const { keys } = body as { keys?: string[] }; + if (!Array.isArray(keys) || keys.length === 0) { + return NextResponse.json({ error: '缺少 keys 参数或为空' }, { status: 400 }); + } + // 过滤掉 from=config 的源,但不报错 + const keysToDelete = keys.filter(key => { + const entry = adminConfig.SourceConfig.find((s) => s.key === key); + return entry && entry.from !== 'config'; + }); + + // 批量删除 + keysToDelete.forEach(key => { + const idx = adminConfig.SourceConfig.findIndex((s) => s.key === key); + if (idx !== -1) { + adminConfig.SourceConfig.splice(idx, 1); + } + }); + + // 检查并清理用户组和用户的权限数组 + if (keysToDelete.length > 0) { + // 清理用户组权限 + if (adminConfig.UserConfig.Tags) { + adminConfig.UserConfig.Tags.forEach(tag => { + if (tag.enabledApis) { + tag.enabledApis = tag.enabledApis.filter(api => !keysToDelete.includes(api)); + } + }); + } + + // 清理用户权限 + adminConfig.UserConfig.Users.forEach(user => { + if (user.enabledApis) { + user.enabledApis = user.enabledApis.filter(api => !keysToDelete.includes(api)); + } + }); + } break; } case 'sort': { diff --git a/src/app/api/admin/user/route.ts b/src/app/api/admin/user/route.ts index 5671ff4..9473f25 100644 --- a/src/app/api/admin/user/route.ts +++ b/src/app/api/admin/user/route.ts @@ -20,6 +20,7 @@ const ACTIONS = [ 'updateUserApis', 'userGroup', 'updateUserGroups', + 'batchUpdateUserGroups', ] as const; export async function POST(request: NextRequest) { @@ -56,8 +57,8 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '参数格式错误' }, { status: 400 }); } - // 用户组操作不需要targetUsername - if (!targetUsername && action !== 'userGroup') { + // 用户组操作和批量操作不需要targetUsername + if (!targetUsername && !['userGroup', 'batchUpdateUserGroups'].includes(action)) { return NextResponse.json({ error: '缺少目标用户名' }, { status: 400 }); } @@ -67,6 +68,7 @@ export async function POST(request: NextRequest) { action !== 'updateUserApis' && action !== 'userGroup' && action !== 'updateUserGroups' && + action !== 'batchUpdateUserGroups' && username === targetUsername ) { return NextResponse.json( @@ -92,11 +94,11 @@ export async function POST(request: NextRequest) { operatorRole = 'admin'; } - // 查找目标用户条目(用户组操作不需要) + // 查找目标用户条目(用户组操作和批量操作不需要) let targetEntry: any = null; let isTargetAdmin = false; - if (action !== 'userGroup' && targetUsername) { + if (!['userGroup', 'batchUpdateUserGroups'].includes(action) && targetUsername) { targetEntry = adminConfig.UserConfig.Users.find( (u) => u.username === targetUsername ); @@ -419,6 +421,38 @@ export async function POST(request: NextRequest) { break; } + case 'batchUpdateUserGroups': { + const { usernames, userGroups } = body as { usernames: string[]; userGroups: string[] }; + + if (!usernames || !Array.isArray(usernames) || usernames.length === 0) { + return NextResponse.json({ error: '缺少用户名列表' }, { status: 400 }); + } + + // 权限检查:站长可批量配置所有人的用户组,管理员只能批量配置普通用户 + if (operatorRole !== 'owner') { + for (const targetUsername of usernames) { + const targetUser = adminConfig.UserConfig.Users.find(u => u.username === targetUsername); + if (targetUser && targetUser.role === 'admin' && targetUsername !== username) { + return NextResponse.json({ error: `管理员无法操作其他管理员 ${targetUsername}` }, { status: 400 }); + } + } + } + + // 批量更新用户组 + for (const targetUsername of usernames) { + const targetUser = adminConfig.UserConfig.Users.find(u => u.username === targetUsername); + if (targetUser) { + if (userGroups && userGroups.length > 0) { + targetUser.tags = userGroups; + } else { + // 如果为空数组或未提供,则删除该字段,表示无用户组 + delete targetUser.tags; + } + } + } + + break; + } default: return NextResponse.json({ error: '未知操作' }, { status: 400 }); } diff --git a/src/app/api/favorites/route.ts b/src/app/api/favorites/route.ts index 9c67323..4bd7724 100644 --- a/src/app/api/favorites/route.ts +++ b/src/app/api/favorites/route.ts @@ -25,12 +25,15 @@ export async function GET(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -76,12 +79,15 @@ export async function POST(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -144,12 +150,15 @@ export async function DELETE(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } diff --git a/src/app/api/playrecords/route.ts b/src/app/api/playrecords/route.ts index f830f29..181ad0a 100644 --- a/src/app/api/playrecords/route.ts +++ b/src/app/api/playrecords/route.ts @@ -18,12 +18,15 @@ export async function GET(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -48,12 +51,15 @@ export async function POST(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -111,12 +117,15 @@ export async function DELETE(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } diff --git a/src/app/api/search/suggestions/route.ts b/src/app/api/search/suggestions/route.ts index 098b276..bd7536d 100644 --- a/src/app/api/search/suggestions/route.ts +++ b/src/app/api/search/suggestions/route.ts @@ -17,16 +17,6 @@ export async function GET(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 - const user = config.UserConfig.Users.find( - (u) => u.username === authInfo.username - ); - if (user && user.banned) { - return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); - } - } - const { searchParams } = new URL(request.url); const query = searchParams.get('q')?.trim(); diff --git a/src/app/api/searchhistory/route.ts b/src/app/api/searchhistory/route.ts index f025ade..9a9e717 100644 --- a/src/app/api/searchhistory/route.ts +++ b/src/app/api/searchhistory/route.ts @@ -24,12 +24,15 @@ export async function GET(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -58,12 +61,15 @@ export async function POST(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -107,12 +113,15 @@ export async function DELETE(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } diff --git a/src/app/api/skipconfigs/route.ts b/src/app/api/skipconfigs/route.ts index cbab459..3762c4a 100644 --- a/src/app/api/skipconfigs/route.ts +++ b/src/app/api/skipconfigs/route.ts @@ -17,12 +17,15 @@ export async function GET(request: NextRequest) { } const config = await getConfig(); - if (config.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = config.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -57,12 +60,15 @@ export async function POST(request: NextRequest) { } const adminConfig = await getConfig(); - if (adminConfig.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = adminConfig.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } } @@ -107,12 +113,15 @@ export async function DELETE(request: NextRequest) { } const adminConfig = await getConfig(); - if (adminConfig.UserConfig.Users) { - // 检查用户是否被封禁 + if (authInfo.username !== process.env.ADMIN_USERNAME) { + // 非站长,检查用户存在或被封禁 const user = adminConfig.UserConfig.Users.find( (u) => u.username === authInfo.username ); - if (user && user.banned) { + if (!user) { + return NextResponse.json({ error: '用户不存在' }, { status: 401 }); + } + if (user.banned) { return NextResponse.json({ error: '用户已被封禁' }, { status: 401 }); } }