用户数据结构变更
This commit is contained in:
@@ -34,14 +34,31 @@ export async function GET(request: NextRequest) {
|
||||
if (username === process.env.USERNAME) {
|
||||
result.Role = 'owner';
|
||||
} else {
|
||||
const user = config.UserConfig.Users.find((u) => u.username === username);
|
||||
if (user && user.role === 'admin' && !user.banned) {
|
||||
result.Role = 'admin';
|
||||
// 优先从新版本获取用户信息
|
||||
const { db } = await import('@/lib/db');
|
||||
const userInfoV2 = await db.getUserInfoV2(username);
|
||||
|
||||
if (userInfoV2) {
|
||||
// 使用新版本用户信息
|
||||
if (userInfoV2.role === 'admin' && !userInfoV2.banned) {
|
||||
result.Role = 'admin';
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ error: '你是管理员吗你就访问?' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ error: '你是管理员吗你就访问?' },
|
||||
{ status: 401 }
|
||||
);
|
||||
// 回退到配置中查找
|
||||
const user = config.UserConfig.Users.find((u) => u.username === username);
|
||||
if (user && user.role === 'admin' && !user.banned) {
|
||||
result.Role = 'admin';
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ error: '你是管理员吗你就访问?' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
76
src/app/api/admin/migrate-users/route.ts
Normal file
76
src/app/api/admin/migrate-users/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable no-console,@typescript-eslint/no-explicit-any */
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: '不支持本地存储进行数据迁移',
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
// 只有站长可以执行迁移
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
const adminConfig = await getConfig();
|
||||
|
||||
// 检查是否有需要迁移的用户(排除站长)
|
||||
const usersToMigrate = adminConfig.UserConfig.Users.filter(
|
||||
u => u.role !== 'owner'
|
||||
);
|
||||
|
||||
if (!usersToMigrate || usersToMigrate.length === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: '没有需要迁移的用户' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 执行迁移
|
||||
await db.migrateUsersFromConfig(adminConfig);
|
||||
|
||||
// 迁移完成后,清空配置中的用户列表
|
||||
adminConfig.UserConfig.Users = [];
|
||||
await db.saveAdminConfig(adminConfig);
|
||||
|
||||
// 更新配置缓存
|
||||
const { setCachedConfig } = await import('@/lib/config');
|
||||
await setCachedConfig(adminConfig);
|
||||
|
||||
return NextResponse.json(
|
||||
{ ok: true, message: '用户数据迁移成功' },
|
||||
{
|
||||
headers: {
|
||||
'Cache-Control': 'no-store',
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('用户数据迁移失败:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: '用户数据迁移失败',
|
||||
details: (error as Error).message,
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -85,24 +85,50 @@ export async function POST(request: NextRequest) {
|
||||
if (username === process.env.USERNAME) {
|
||||
operatorRole = 'owner';
|
||||
} else {
|
||||
const userEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === username
|
||||
);
|
||||
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
// 优先从新版本获取用户信息
|
||||
const operatorInfo = await db.getUserInfoV2(username);
|
||||
if (operatorInfo) {
|
||||
if (operatorInfo.role !== 'admin' || operatorInfo.banned) {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
operatorRole = 'admin';
|
||||
} else {
|
||||
// 回退到配置中查找
|
||||
const userEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === username
|
||||
);
|
||||
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
operatorRole = 'admin';
|
||||
}
|
||||
operatorRole = 'admin';
|
||||
}
|
||||
|
||||
// 查找目标用户条目(用户组操作和批量操作不需要)
|
||||
let targetEntry: any = null;
|
||||
let isTargetAdmin = false;
|
||||
let targetUserV2: any = null;
|
||||
|
||||
if (!['userGroup', 'batchUpdateUserGroups'].includes(action) && targetUsername) {
|
||||
// 先从配置中查找
|
||||
targetEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === targetUsername
|
||||
);
|
||||
|
||||
// 如果配置中没有,从新版本存储中查找
|
||||
if (!targetEntry) {
|
||||
targetUserV2 = await db.getUserInfoV2(targetUsername);
|
||||
if (targetUserV2) {
|
||||
// 构造一个兼容的targetEntry对象
|
||||
targetEntry = {
|
||||
username: targetUsername,
|
||||
role: targetUserV2.role,
|
||||
banned: targetUserV2.banned,
|
||||
tags: targetUserV2.tags,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
targetEntry &&
|
||||
targetEntry.role === 'owner' &&
|
||||
@@ -120,33 +146,35 @@ export async function POST(request: NextRequest) {
|
||||
if (targetEntry) {
|
||||
return NextResponse.json({ error: '用户已存在' }, { status: 400 });
|
||||
}
|
||||
// 检查新版本中是否已存在
|
||||
const existsV2 = await db.checkUserExistV2(targetUsername!);
|
||||
if (existsV2) {
|
||||
return NextResponse.json({ error: '用户已存在' }, { status: 400 });
|
||||
}
|
||||
if (!targetPassword) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少目标用户密码' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
await db.registerUser(targetUsername!, targetPassword);
|
||||
|
||||
// 获取用户组信息
|
||||
const { userGroup } = body as { userGroup?: string };
|
||||
const tags = userGroup && userGroup.trim() ? [userGroup] : undefined;
|
||||
|
||||
// 更新配置
|
||||
const newUser: any = {
|
||||
// 使用新版本创建用户
|
||||
await db.createUserV2(targetUsername!, targetPassword, 'user', tags);
|
||||
|
||||
// 同时在旧版本存储中创建(保持兼容性)
|
||||
await db.registerUser(targetUsername!, targetPassword);
|
||||
|
||||
// 不再更新配置,因为用户已经存储在新版本中
|
||||
// 构造一个虚拟的targetEntry用于后续逻辑
|
||||
targetEntry = {
|
||||
username: targetUsername!,
|
||||
role: 'user',
|
||||
tags,
|
||||
};
|
||||
|
||||
// 如果指定了用户组,添加到tags中
|
||||
if (userGroup && userGroup.trim()) {
|
||||
newUser.tags = [userGroup];
|
||||
}
|
||||
|
||||
adminConfig.UserConfig.Users.push(newUser);
|
||||
targetEntry =
|
||||
adminConfig.UserConfig.Users[
|
||||
adminConfig.UserConfig.Users.length - 1
|
||||
];
|
||||
break;
|
||||
}
|
||||
case 'ban': {
|
||||
@@ -165,7 +193,9 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
}
|
||||
targetEntry.banned = true;
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { banned: true });
|
||||
break;
|
||||
}
|
||||
case 'unban': {
|
||||
@@ -183,7 +213,9 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
}
|
||||
targetEntry.banned = false;
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { banned: false });
|
||||
break;
|
||||
}
|
||||
case 'setAdmin': {
|
||||
@@ -205,7 +237,9 @@ export async function POST(request: NextRequest) {
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
targetEntry.role = 'admin';
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { role: 'admin' });
|
||||
break;
|
||||
}
|
||||
case 'cancelAdmin': {
|
||||
@@ -227,7 +261,9 @@ export async function POST(request: NextRequest) {
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
targetEntry.role = 'user';
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { role: 'user' });
|
||||
break;
|
||||
}
|
||||
case 'changePassword': {
|
||||
@@ -260,6 +296,9 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 使用新版本修改密码(SHA256加密)
|
||||
await db.changePasswordV2(targetUsername!, targetPassword);
|
||||
// 同时更新旧版本(保持兼容性)
|
||||
await db.changePassword(targetUsername!, targetPassword);
|
||||
break;
|
||||
}
|
||||
@@ -286,16 +325,11 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 只删除V2存储中的用户
|
||||
await db.deleteUserV2(targetUsername!);
|
||||
// 同时删除旧版本(保持兼容性)
|
||||
await db.deleteUser(targetUsername!);
|
||||
|
||||
// 从配置中移除用户
|
||||
const userIndex = adminConfig.UserConfig.Users.findIndex(
|
||||
(u) => u.username === targetUsername
|
||||
);
|
||||
if (userIndex > -1) {
|
||||
adminConfig.UserConfig.Users.splice(userIndex, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'updateUserApis': {
|
||||
@@ -320,13 +354,10 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 更新用户的采集源权限
|
||||
if (enabledApis && enabledApis.length > 0) {
|
||||
targetEntry.enabledApis = enabledApis;
|
||||
} else {
|
||||
// 如果为空数组或未提供,则删除该字段,表示无限制
|
||||
delete targetEntry.enabledApis;
|
||||
}
|
||||
// 更新V2存储中的采集源权限
|
||||
await db.updateUserInfoV2(targetUsername!, {
|
||||
enabledApis: enabledApis && enabledApis.length > 0 ? enabledApis : []
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -368,19 +399,17 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: '用户组不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 查找使用该用户组的所有用户
|
||||
const affectedUsers: string[] = [];
|
||||
adminConfig.UserConfig.Users.forEach(user => {
|
||||
if (user.tags && user.tags.includes(groupName)) {
|
||||
affectedUsers.push(user.username);
|
||||
// 从用户的tags中移除该用户组
|
||||
user.tags = user.tags.filter(tag => tag !== groupName);
|
||||
// 如果用户没有其他标签了,删除tags字段
|
||||
if (user.tags.length === 0) {
|
||||
delete user.tags;
|
||||
}
|
||||
// 查找使用该用户组的所有用户(从V2存储中查找)
|
||||
const affectedUsers = await db.getUsersByTag(groupName);
|
||||
|
||||
// 从用户的tags中移除该用户组
|
||||
for (const username of affectedUsers) {
|
||||
const userInfo = await db.getUserInfoV2(username);
|
||||
if (userInfo && userInfo.tags) {
|
||||
const newTags = userInfo.tags.filter(tag => tag !== groupName);
|
||||
await db.updateUserInfoV2(username, { tags: newTags });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 删除用户组
|
||||
adminConfig.UserConfig.Tags.splice(groupIndex, 1);
|
||||
@@ -413,10 +442,11 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// 更新用户的用户组
|
||||
if (userGroups && userGroups.length > 0) {
|
||||
targetEntry.tags = userGroups;
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { tags: userGroups });
|
||||
} else {
|
||||
// 如果为空数组或未提供,则删除该字段,表示无用户组
|
||||
delete targetEntry.tags;
|
||||
await db.updateUserInfoV2(targetUsername!, { tags: [] });
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -431,7 +461,20 @@ export async function POST(request: NextRequest) {
|
||||
// 权限检查:站长可批量配置所有人的用户组,管理员只能批量配置普通用户
|
||||
if (operatorRole !== 'owner') {
|
||||
for (const targetUsername of usernames) {
|
||||
const targetUser = adminConfig.UserConfig.Users.find(u => u.username === targetUsername);
|
||||
// 先从配置中查找
|
||||
let targetUser = adminConfig.UserConfig.Users.find(u => u.username === targetUsername);
|
||||
// 如果配置中没有,从V2存储中查找
|
||||
if (!targetUser) {
|
||||
const userV2 = await db.getUserInfoV2(targetUsername);
|
||||
if (userV2) {
|
||||
targetUser = {
|
||||
username: targetUsername,
|
||||
role: userV2.role,
|
||||
banned: userV2.banned,
|
||||
tags: userV2.tags,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (targetUser && targetUser.role === 'admin' && targetUsername !== username) {
|
||||
return NextResponse.json({ error: `管理员无法操作其他管理员 ${targetUsername}` }, { status: 400 });
|
||||
}
|
||||
@@ -440,14 +483,11 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// 批量更新用户组
|
||||
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;
|
||||
}
|
||||
// 只更新V2存储
|
||||
if (userGroups && userGroups.length > 0) {
|
||||
await db.updateUserInfoV2(targetUsername, { tags: userGroups });
|
||||
} else {
|
||||
await db.updateUserInfoV2(targetUsername, { tags: [] });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
129
src/app/api/admin/users/route.ts
Normal file
129
src/app/api/admin/users/route.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/* eslint-disable no-console,@typescript-eslint/no-explicit-any */
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: '不支持本地存储进行用户列表查询',
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
const adminConfig = await getConfig();
|
||||
|
||||
// 判定操作者角色
|
||||
let operatorRole: 'owner' | 'admin' | 'user' = 'user';
|
||||
if (authInfo.username === process.env.USERNAME) {
|
||||
operatorRole = 'owner';
|
||||
} else {
|
||||
// 优先从新版本获取用户信息
|
||||
const operatorInfo = await db.getUserInfoV2(authInfo.username);
|
||||
if (operatorInfo) {
|
||||
operatorRole = operatorInfo.role;
|
||||
} else {
|
||||
// 回退到配置中查找
|
||||
const userEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (userEntry) {
|
||||
operatorRole = userEntry.role;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有站长和管理员可以查看用户列表
|
||||
if (operatorRole !== 'owner' && operatorRole !== 'admin') {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
|
||||
// 获取分页参数
|
||||
const { searchParams } = new URL(request.url);
|
||||
const page = parseInt(searchParams.get('page') || '1', 10);
|
||||
const limit = parseInt(searchParams.get('limit') || '10', 10);
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 获取用户列表(优先使用新版本)
|
||||
const result = await db.getUserListV2(offset, limit, process.env.USERNAME);
|
||||
|
||||
if (result.users.length > 0) {
|
||||
// 使用新版本数据
|
||||
return NextResponse.json(
|
||||
{
|
||||
users: result.users,
|
||||
total: result.total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(result.total / limit),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Cache-Control': 'no-store',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 回退到配置中的用户列表
|
||||
const configUsers = adminConfig.UserConfig.Users || [];
|
||||
const total = configUsers.length;
|
||||
|
||||
// 排序:站长始终在第一位,其他用户按用户名排序
|
||||
const sortedUsers = [...configUsers].sort((a, b) => {
|
||||
if (a.username === process.env.USERNAME) return -1;
|
||||
if (b.username === process.env.USERNAME) return 1;
|
||||
return a.username.localeCompare(b.username);
|
||||
});
|
||||
|
||||
// 分页
|
||||
const paginatedUsers = sortedUsers.slice(offset, offset + limit);
|
||||
|
||||
// 转换为统一格式
|
||||
const users = paginatedUsers.map((u) => ({
|
||||
username: u.username,
|
||||
role: u.role,
|
||||
banned: u.banned || false,
|
||||
tags: u.tags,
|
||||
created_at: 0, // 配置中没有创建时间
|
||||
}));
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
users,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Cache-Control': 'no-store',
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: '获取用户列表失败',
|
||||
details: (error as Error).message,
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -148,15 +148,41 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
// 检查用户是否已存在(通过OIDC sub查找)
|
||||
const existingUser = config.UserConfig.Users.find((u: any) => u.oidcSub === oidcSub);
|
||||
// 优先使用新版本查找
|
||||
let username = await db.getUserByOidcSub(oidcSub);
|
||||
let userRole: 'owner' | 'admin' | 'user' = 'user';
|
||||
|
||||
if (existingUser) {
|
||||
if (username) {
|
||||
// 从新版本获取用户信息
|
||||
const userInfoV2 = await db.getUserInfoV2(username);
|
||||
if (userInfoV2) {
|
||||
userRole = userInfoV2.role;
|
||||
// 检查用户是否被封禁
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.redirect(
|
||||
new URL('/login?error=' + encodeURIComponent('用户被封禁'), origin)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 回退到配置中查找
|
||||
const existingUser = config.UserConfig.Users.find((u: any) => u.oidcSub === oidcSub);
|
||||
if (existingUser) {
|
||||
username = existingUser.username;
|
||||
userRole = existingUser.role || 'user';
|
||||
// 检查用户是否被封禁
|
||||
if (existingUser.banned) {
|
||||
return NextResponse.redirect(
|
||||
new URL('/login?error=' + encodeURIComponent('用户被封禁'), origin)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (username) {
|
||||
// 用户已存在,直接登录
|
||||
const response = NextResponse.redirect(new URL('/', origin));
|
||||
const cookieValue = await generateAuthCookie(
|
||||
existingUser.username,
|
||||
existingUser.role || 'user'
|
||||
);
|
||||
const cookieValue = await generateAuthCookie(username, userRole);
|
||||
const expires = new Date();
|
||||
expires.setDate(expires.getDate() + 7);
|
||||
|
||||
|
||||
@@ -110,7 +110,20 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
// 检查用户名是否已存在(优先使用新版本)
|
||||
let userExists = await db.checkUserExistV2(username);
|
||||
if (!userExists) {
|
||||
// 回退到旧版本检查
|
||||
userExists = await db.checkUserExist(username);
|
||||
}
|
||||
if (userExists) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名已存在' },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
// 检查配置中是否已存在
|
||||
const existingUser = config.UserConfig.Users.find((u) => u.username === username);
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
@@ -119,9 +132,16 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 检查OIDC sub是否已被使用
|
||||
const existingOIDCUser = config.UserConfig.Users.find((u: any) => u.oidcSub === oidcSession.sub);
|
||||
if (existingOIDCUser) {
|
||||
// 检查OIDC sub是否已被使用(优先使用新版本)
|
||||
let existingOIDCUsername = await db.getUserByOidcSub(oidcSession.sub);
|
||||
if (!existingOIDCUsername) {
|
||||
// 回退到配置中查找
|
||||
const existingOIDCUser = config.UserConfig.Users.find((u: any) => u.oidcSub === oidcSession.sub);
|
||||
if (existingOIDCUser) {
|
||||
existingOIDCUsername = existingOIDCUser.username;
|
||||
}
|
||||
}
|
||||
if (existingOIDCUsername) {
|
||||
return NextResponse.json(
|
||||
{ error: '该OIDC账号已被注册' },
|
||||
{ status: 409 }
|
||||
@@ -132,9 +152,19 @@ export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 生成随机密码(OIDC用户不需要密码登录)
|
||||
const randomPassword = crypto.randomUUID();
|
||||
|
||||
// 获取默认用户组
|
||||
const defaultTags = siteConfig.DefaultUserTags && siteConfig.DefaultUserTags.length > 0
|
||||
? siteConfig.DefaultUserTags
|
||||
: undefined;
|
||||
|
||||
// 使用新版本创建用户(带SHA256加密和OIDC绑定)
|
||||
await db.createUserV2(username, randomPassword, 'user', defaultTags, oidcSession.sub);
|
||||
|
||||
// 同时在旧版本存储中创建(保持兼容性)
|
||||
await db.registerUser(username, randomPassword);
|
||||
|
||||
// 将用户添加到配置中
|
||||
// 将用户添加到配置中(保持兼容性)
|
||||
const newUser: any = {
|
||||
username: username,
|
||||
role: 'user',
|
||||
@@ -143,8 +173,8 @@ export async function POST(request: NextRequest) {
|
||||
};
|
||||
|
||||
// 如果配置了默认用户组,分配给新用户
|
||||
if (siteConfig.DefaultUserTags && siteConfig.DefaultUserTags.length > 0) {
|
||||
newUser.tags = siteConfig.DefaultUserTags;
|
||||
if (defaultTags) {
|
||||
newUser.tags = defaultTags;
|
||||
}
|
||||
|
||||
config.UserConfig.Users.push(newUser);
|
||||
|
||||
@@ -45,8 +45,8 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
await db.changePassword(username, newPassword);
|
||||
// 修改密码(只更新V2存储)
|
||||
await db.changePasswordV2(username, newPassword);
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
} catch (error) {
|
||||
|
||||
@@ -16,16 +16,13 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -55,16 +52,13 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
const adminConfig = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,13 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -78,16 +75,13 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -149,16 +143,13 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,44 +217,69 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const config = await getConfig();
|
||||
const user = config.UserConfig.Users.find((u) => u.username === username);
|
||||
if (user && user.banned) {
|
||||
|
||||
// 优先使用新版本的用户验证
|
||||
let pass = false;
|
||||
let userRole: 'owner' | 'admin' | 'user' = 'user';
|
||||
let isBanned = false;
|
||||
|
||||
// 尝试使用新版本验证
|
||||
const userInfoV2 = await db.getUserInfoV2(username);
|
||||
|
||||
if (userInfoV2) {
|
||||
// 使用新版本验证
|
||||
pass = await db.verifyUserV2(username, password);
|
||||
userRole = userInfoV2.role;
|
||||
isBanned = userInfoV2.banned;
|
||||
} else {
|
||||
// 回退到旧版本验证
|
||||
try {
|
||||
pass = await db.verifyUser(username, password);
|
||||
// 从配置中获取角色和封禁状态
|
||||
if (user) {
|
||||
userRole = user.role;
|
||||
isBanned = user.banned || false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('数据库验证失败', err);
|
||||
return NextResponse.json({ error: '数据库错误' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// 检查用户是否被封禁
|
||||
if (isBanned) {
|
||||
return NextResponse.json({ error: '用户被封禁' }, { status: 401 });
|
||||
}
|
||||
|
||||
// 校验用户密码
|
||||
try {
|
||||
const pass = await db.verifyUser(username, password);
|
||||
if (!pass) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// 验证成功,设置认证cookie
|
||||
const response = NextResponse.json({ ok: true });
|
||||
const cookieValue = await generateAuthCookie(
|
||||
username,
|
||||
password,
|
||||
user?.role || 'user',
|
||||
false
|
||||
); // 数据库模式不包含 password
|
||||
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 });
|
||||
if (!pass) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// 验证成功,设置认证cookie
|
||||
const response = NextResponse.json({ ok: true });
|
||||
const cookieValue = await generateAuthCookie(
|
||||
username,
|
||||
password,
|
||||
userRole,
|
||||
false
|
||||
); // 数据库模式不包含 password
|
||||
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, // 根据协议自动设置
|
||||
});
|
||||
|
||||
console.log(`Cookie已设置`);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('登录接口异常', error);
|
||||
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
|
||||
|
||||
@@ -17,16 +17,13 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -50,16 +47,13 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -116,16 +110,13 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,20 @@ export async function POST(req: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 检查用户是否已存在
|
||||
// 检查用户是否已存在(优先使用新版本)
|
||||
let userExists = await db.checkUserExistV2(username);
|
||||
if (!userExists) {
|
||||
// 回退到旧版本检查
|
||||
userExists = await db.checkUserExist(username);
|
||||
}
|
||||
if (userExists) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名已存在' },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
// 检查配置中是否已存在
|
||||
const existingUser = config.UserConfig.Users.find((u) => u.username === username);
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
@@ -131,24 +144,31 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
// 创建用户
|
||||
try {
|
||||
// 1. 在数据库中创建用户密码
|
||||
// 1. 使用新版本创建用户(带SHA256加密)
|
||||
const defaultTags = siteConfig.DefaultUserTags && siteConfig.DefaultUserTags.length > 0
|
||||
? siteConfig.DefaultUserTags
|
||||
: undefined;
|
||||
|
||||
await db.createUserV2(username, password, 'user', defaultTags);
|
||||
|
||||
// 2. 同时在旧版本存储中创建(保持兼容性)
|
||||
await db.registerUser(username, password);
|
||||
|
||||
// 2. 将用户添加到管理员配置的用户列表中
|
||||
// 3. 将用户添加到管理员配置的用户列表中(保持兼容性)
|
||||
const newUser: any = {
|
||||
username: username,
|
||||
role: 'user',
|
||||
banned: false,
|
||||
};
|
||||
|
||||
// 3. 如果配置了默认用户组,分配给新用户
|
||||
if (siteConfig.DefaultUserTags && siteConfig.DefaultUserTags.length > 0) {
|
||||
newUser.tags = siteConfig.DefaultUserTags;
|
||||
// 4. 如果配置了默认用户组,分配给新用户
|
||||
if (defaultTags) {
|
||||
newUser.tags = defaultTags;
|
||||
}
|
||||
|
||||
config.UserConfig.Users.push(newUser);
|
||||
|
||||
// 4. 保存更新后的配置
|
||||
// 5. 保存更新后的配置
|
||||
await db.saveAdminConfig(config);
|
||||
|
||||
// 注册成功
|
||||
|
||||
@@ -23,16 +23,13 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -60,16 +57,13 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -112,16 +106,13 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,13 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = config.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -59,16 +56,13 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
const adminConfig = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -112,16 +106,13 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
const adminConfig = await getConfig();
|
||||
if (authInfo.username !== process.env.ADMIN_USERNAME) {
|
||||
if (authInfo.username !== process.env.USERNAME) {
|
||||
// 非站长,检查用户存在或被封禁
|
||||
const user = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === authInfo.username
|
||||
);
|
||||
if (!user) {
|
||||
const userInfoV2 = await db.getUserInfoV2(authInfo.username);
|
||||
if (!userInfoV2) {
|
||||
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
|
||||
}
|
||||
if (user.banned) {
|
||||
if (userInfoV2.banned) {
|
||||
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user