feat: add batch source and user operates

This commit is contained in:
shinya
2025-08-22 22:44:15 +08:00
parent aec039e42e
commit ca9a7403eb
8 changed files with 626 additions and 74 deletions

View File

@@ -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': {

View File

@@ -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 });
}

View File

@@ -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 });
}
}

View File

@@ -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 });
}
}

View File

@@ -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();

View File

@@ -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 });
}
}

View File

@@ -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 });
}
}