修复bug

This commit is contained in:
2025-07-08 16:44:04 +08:00
parent aa2416c5d6
commit 1af79c4111
22 changed files with 400 additions and 1036 deletions

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine
FROM node:18-alpine3.18
# 设置工作目录
WORKDIR /app
@@ -8,17 +8,21 @@ RUN apk add --no-cache \
python3 \
make \
g++ \
curl
curl \
openssl1.1-compat
# 复制 package.json 和 package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production && npm cache clean --force
RUN npm install
# 复制源代码
COPY . .
# 构建项目
RUN npm run build
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
@@ -30,9 +34,10 @@ USER nodejs
# 暴露端口
EXPOSE 3001
RUN npx prisma generate
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3001/health || exit 1
# 启动命令
CMD ["npm", "run", "dev"]
CMD [ "sh", "-c", "npm run db:push && npm run db:seed && npm run start" ]

View File

@@ -14,8 +14,6 @@ model User {
id String @id @default(cuid())
username String @unique
password String
firstName String?
lastName String?
isActive Boolean @default(true)
isAdmin Boolean @default(false)
lastLoginAt DateTime?

View File

@@ -9,138 +9,37 @@ async function main() {
// 创建管理员用户
const adminPassword = await bcrypt.hash('admin123', 12);
const admin = await prisma.user.upsert({
where: { email: 'admin@pandora.com' },
where: { username: 'admin' },
update: {},
create: {
email: 'admin@pandora.com',
username: 'admin',
password: adminPassword,
firstName: '管理员',
lastName: '系统',
isAdmin: true,
isActive: true,
emailVerified: true,
},
});
// 创建测试用户
const userPassword = await bcrypt.hash('user123', 12);
const user = await prisma.user.upsert({
where: { email: 'user@pandora.com' },
update: {},
create: {
email: 'user@pandora.com',
username: 'testuser',
password: userPassword,
firstName: '测试',
lastName: '用户',
isAdmin: false,
isActive: true,
emailVerified: true,
},
});
// 创建网站账号
const accounts = [
{
website: 'claude.ai',
accountName: 'claude_pro_1',
token: 'sk-ant-api03-xxx-claude-pro-1',
maxUsers: 3,
currentUsers: 0,
},
{
website: 'openai.com',
accountName: 'gpt4_plus_1',
token: 'sk-xxx-gpt4-plus-1',
maxUsers: 2,
currentUsers: 0,
},
{
website: 'gemini.google.com',
accountName: 'gemini_pro_1',
token: 'AIzaSyCxxx-gemini-pro-1',
maxUsers: 1,
currentUsers: 0,
},
];
for (const accountData of accounts) {
await prisma.websiteAccount.upsert({
where: {
website_accountName: {
website: accountData.website,
accountName: accountData.accountName,
}
},
update: {},
create: accountData,
});
}
// 为管理员分配所有账号
const allAccounts = await prisma.websiteAccount.findMany();
for (const account of allAccounts) {
await prisma.accountAssignment.upsert({
where: {
userId_accountId: {
userId: admin.id,
accountId: account.id,
}
},
update: {},
create: {
userId: admin.id,
accountId: account.id,
isActive: true,
},
});
}
// 为用户分配部分账号
const userAccounts = await prisma.websiteAccount.findMany({
where: {
website: {
in: ['claude.ai', 'openai.com']
}
}
});
for (const account of userAccounts) {
await prisma.accountAssignment.upsert({
where: {
userId_accountId: {
userId: user.id,
accountId: account.id,
}
},
update: {},
create: {
userId: user.id,
accountId: account.id,
isActive: true,
},
});
}
// 更新账号当前用户数
for (const account of allAccounts) {
const userCount = await prisma.accountAssignment.count({
where: {
accountId: account.id,
isActive: true,
}
});
await prisma.websiteAccount.update({
where: { id: account.id },
data: { currentUsers: userCount }
});
}
// const allAccounts = await prisma.websiteAccount.findMany();
// for (const account of allAccounts) {
// await prisma.accountAssignment.upsert({
// where: {
// userId_accountId: {
// userId: admin.id,
// accountId: account.id,
// }
// },
// update: {},
// create: {
// userId: admin.id,
// accountId: account.id,
// isActive: true,
// },
// });
// }
console.log('数据库初始化完成!');
console.log('管理员账户:', admin.email, '密码: admin123');
console.log('测试用户账户:', user.email, '密码: user123');
console.log('管理员账户:', admin.username, '密码: admin123');
}
main()

View File

@@ -51,8 +51,6 @@ export const accountController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
}
}
}

View File

@@ -46,7 +46,7 @@ async function createSession(userId: string, token: string, req: Request) {
export const authController = {
// 用户注册
async register(req: Request, res: Response) {
const { username, password, confirmPassword, firstName, lastName } = req.body;
const { username, password, confirmPassword } = req.body;
// 验证密码确认
if (password !== confirmPassword) {
@@ -74,8 +74,6 @@ export const authController = {
data: {
username,
password: hashedPassword,
firstName,
lastName,
isActive: false, // 新注册用户默认为禁用状态
},
});
@@ -97,8 +95,6 @@ export const authController = {
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
isActive: user.isActive,
}
@@ -237,8 +233,6 @@ export const authController = {
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
}
});
@@ -283,8 +277,6 @@ export const authController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
lastLoginAt: true,

View File

@@ -2,6 +2,7 @@ import { Request, Response } from 'express';
import { prisma } from '../config/database';
import { AuthRequest } from '../middleware/auth';
import bcrypt from 'bcryptjs';
import type { PrismaClient } from '@prisma/client';
export const userController = {
// 获取所有用户 (管理员)
@@ -18,8 +19,6 @@ export const userController = {
if (search) {
where.OR = [
{ username: { contains: search as string, mode: 'insensitive' } },
{ firstName: { contains: search as string, mode: 'insensitive' } },
{ lastName: { contains: search as string, mode: 'insensitive' } }
];
}
@@ -40,11 +39,8 @@ export const userController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
totpEnabled: true,
lastLoginAt: true,
createdAt: true,
accountAssignments: {
@@ -65,12 +61,9 @@ export const userController = {
users: users.map((user: any) => ({
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
role: user.isAdmin ? 'admin' : 'user',
isAdmin: user.isAdmin,
isActive: user.isActive,
totpEnabled: user.totpEnabled,
lastLoginAt: user.lastLoginAt,
createdAt: user.createdAt,
accounts: user.accountAssignments.map((assignment: any) => assignment.accountId)
@@ -102,11 +95,8 @@ export const userController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
totpEnabled: true,
lastLoginAt: true,
createdAt: true,
}
@@ -120,11 +110,8 @@ export const userController = {
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
isActive: user.isActive,
totpEnabled: user.totpEnabled,
lastLoginAt: user.lastLoginAt,
createdAt: user.createdAt
}
@@ -134,9 +121,7 @@ export const userController = {
// 更新用户信息
async updateUser(req: AuthRequest, res: Response) {
const { id } = req.params;
const { username, role, firstName, lastName, isActive, loginAttempts } = req.body;
console.log('收到更新请求:', req.body);
const { username, role, isActive, loginAttempts } = req.body;
if (!id) {
return res.status(400).json({ error: '用户ID是必需的' });
@@ -157,9 +142,6 @@ export const userController = {
if (typeof isActive === 'boolean') updateData.isActive = isActive;
}
// 普通用户可以修改这些字段
if (firstName !== undefined) updateData.firstName = firstName;
if (lastName !== undefined) updateData.lastName = lastName;
// 新增:处理密码修改
if (req.body.password && typeof req.body.password === 'string' && req.body.password.trim() !== '') {
@@ -191,11 +173,8 @@ export const userController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
totpEnabled: true,
lastLoginAt: true,
createdAt: true,
password: true,
@@ -207,11 +186,8 @@ export const userController = {
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
isActive: user.isActive,
totpEnabled: user.totpEnabled,
lastLoginAt: user.lastLoginAt,
createdAt: user.createdAt,
password: user.password,
@@ -254,7 +230,7 @@ export const userController = {
}
// 使用事务来确保数据一致性
await prisma.$transaction(async (tx) => {
await prisma.$transaction(async (tx: PrismaClient) => {
// 删除用户现有的所有账号分配
await tx.accountAssignment.deleteMany({
where: { userId: id }
@@ -336,11 +312,8 @@ export const userController = {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
totpEnabled: true,
lastLoginAt: true,
createdAt: true,
}
@@ -351,12 +324,9 @@ export const userController = {
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
role: user.isAdmin ? 'admin' : 'user',
isAdmin: user.isAdmin,
isActive: user.isActive,
totpEnabled: user.totpEnabled,
lastLoginAt: user.lastLoginAt,
createdAt: user.createdAt
}

View File

@@ -16,8 +16,6 @@ router.post('/register', [
}
return true;
}),
body('firstName').optional().isLength({ max: 50 }),
body('lastName').optional().isLength({ max: 50 }),
validateRequest
], authController.register);
@@ -31,15 +29,6 @@ router.post('/login', [
// Logout
router.post('/logout', authMiddleware, authController.logout);
// Setup TOTP
router.post('/setup-totp', authMiddleware, authController.setupTOTP);
// Verify TOTP
router.post('/verify-totp', [
body('token').notEmpty(),
validateRequest
], authMiddleware, authController.verifyTOTP);
// Get current user
router.get('/me', authMiddleware, authController.getCurrentUser);