修复bug
This commit is contained in:
@@ -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" ]
|
||||
@@ -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?
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -51,8 +51,6 @@ export const accountController = {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user