first commit
This commit is contained in:
545
backend/src/controllers/accountController.ts
Normal file
545
backend/src/controllers/accountController.ts
Normal file
@@ -0,0 +1,545 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma } from '../config/database';
|
||||
import { AuthRequest } from '../middleware/authMiddleware';
|
||||
|
||||
export const accountController = {
|
||||
// 获取所有账号 (管理员)
|
||||
async getAllAccounts(req: Request, res: Response) {
|
||||
const accounts = await prisma.websiteAccount.findMany({
|
||||
include: {
|
||||
accountAssignments: {
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return res.json({
|
||||
accounts: accounts.map((account: any) => ({
|
||||
id: account.id,
|
||||
website: account.website,
|
||||
username: account.accountName,
|
||||
token: account.token,
|
||||
isActive: account.isActive,
|
||||
createdAt: account.createdAt,
|
||||
updatedAt: account.updatedAt,
|
||||
assignedUsers: account.accountAssignments.map((aa: any) => aa.user)
|
||||
}))
|
||||
});
|
||||
},
|
||||
|
||||
// 根据ID获取账号 (管理员)
|
||||
async getAccountById(req: Request, res: Response) {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
return res.status(400).json({ error: '账号ID是必需的' });
|
||||
}
|
||||
|
||||
const account = await prisma.websiteAccount.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
accountAssignments: {
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return res.status(404).json({ error: '账号不存在' });
|
||||
}
|
||||
|
||||
return res.json({
|
||||
account: {
|
||||
id: account.id,
|
||||
website: account.website,
|
||||
username: account.accountName,
|
||||
token: account.token,
|
||||
isActive: account.isActive,
|
||||
createdAt: account.createdAt,
|
||||
updatedAt: account.updatedAt,
|
||||
assignedUsers: account.accountAssignments.map((aa: any) => aa.user)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 创建新账号 (管理员)
|
||||
async createAccount(req: Request, res: Response) {
|
||||
const { website, accountName, token, isActive, maxUsers } = req.body;
|
||||
|
||||
if (!website || !accountName || !token) {
|
||||
return res.status(400).json({ error: '网站、账号名称和token是必需的' });
|
||||
}
|
||||
|
||||
// 检查账号是否已存在
|
||||
const existingAccount = await prisma.websiteAccount.findFirst({
|
||||
where: {
|
||||
website,
|
||||
accountName
|
||||
}
|
||||
});
|
||||
|
||||
if (existingAccount) {
|
||||
return res.status(400).json({ error: '账号已存在' });
|
||||
}
|
||||
|
||||
const account = await prisma.websiteAccount.create({
|
||||
data: {
|
||||
website,
|
||||
accountName,
|
||||
token,
|
||||
isActive: isActive !== undefined ? isActive : true,
|
||||
maxUsers: maxUsers || 1,
|
||||
currentUsers: 0,
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
message: '账号创建成功',
|
||||
account: {
|
||||
id: account.id,
|
||||
website: account.website,
|
||||
username: account.accountName,
|
||||
token: account.token,
|
||||
isActive: account.isActive,
|
||||
createdAt: account.createdAt,
|
||||
updatedAt: account.updatedAt
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 更新账号 (管理员)
|
||||
async updateAccount(req: Request, res: Response) {
|
||||
const { id } = req.params;
|
||||
const { website, accountName, token, maxUsers, isActive } = req.body;
|
||||
|
||||
if (!id) {
|
||||
return res.status(400).json({ error: '账号ID是必需的' });
|
||||
}
|
||||
|
||||
const account = await prisma.websiteAccount.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return res.status(404).json({ error: '账号不存在' });
|
||||
}
|
||||
|
||||
// 检查是否与其他账号重复(排除当前账号)
|
||||
if (website && accountName) {
|
||||
const existingAccount = await prisma.websiteAccount.findFirst({
|
||||
where: {
|
||||
website,
|
||||
accountName,
|
||||
id: { not: id }
|
||||
}
|
||||
});
|
||||
|
||||
if (existingAccount) {
|
||||
return res.status(400).json({ error: '该网站和账号名称组合已存在' });
|
||||
}
|
||||
}
|
||||
|
||||
const updatedAccount = await prisma.websiteAccount.update({
|
||||
where: { id },
|
||||
data: {
|
||||
website,
|
||||
accountName,
|
||||
token,
|
||||
maxUsers,
|
||||
isActive,
|
||||
}
|
||||
});
|
||||
|
||||
return res.json({
|
||||
message: '账号更新成功',
|
||||
account: {
|
||||
id: updatedAccount.id,
|
||||
website: updatedAccount.website,
|
||||
username: updatedAccount.accountName,
|
||||
token: updatedAccount.token,
|
||||
isActive: updatedAccount.isActive,
|
||||
createdAt: updatedAccount.createdAt,
|
||||
updatedAt: updatedAccount.updatedAt
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 删除账号 (管理员)
|
||||
async deleteAccount(req: Request, res: Response) {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
return res.status(400).json({ error: '账号ID是必需的' });
|
||||
}
|
||||
|
||||
const account = await prisma.websiteAccount.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return res.status(404).json({ error: '账号不存在' });
|
||||
}
|
||||
|
||||
// 删除账号相关的所有分配
|
||||
await prisma.$transaction([
|
||||
prisma.accountAssignment.deleteMany({ where: { accountId: id } }),
|
||||
prisma.websiteAccount.delete({ where: { id } })
|
||||
]);
|
||||
|
||||
return res.json({ message: '账号删除成功' });
|
||||
},
|
||||
|
||||
// 获取用户的已分配账号
|
||||
async getUserAccounts(req: AuthRequest, res: Response) {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: '未授权' });
|
||||
}
|
||||
|
||||
const assignments = await prisma.accountAssignment.findMany({
|
||||
where: {
|
||||
userId: req.user.id,
|
||||
isActive: true,
|
||||
OR: [
|
||||
{ expiresAt: null },
|
||||
{ expiresAt: { gt: new Date() } }
|
||||
]
|
||||
},
|
||||
include: {
|
||||
account: {
|
||||
select: {
|
||||
id: true,
|
||||
website: true,
|
||||
accountName: true,
|
||||
token: true,
|
||||
isActive: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return res.json({
|
||||
accounts: assignments.map((aa: any) => ({
|
||||
id: aa.account.id,
|
||||
website: aa.account.website,
|
||||
username: aa.account.accountName,
|
||||
token: aa.account.token,
|
||||
isActive: aa.account.isActive,
|
||||
createdAt: aa.account.createdAt,
|
||||
updatedAt: aa.account.updatedAt,
|
||||
assignedAt: aa.assignedAt,
|
||||
expiresAt: aa.expiresAt,
|
||||
}))
|
||||
});
|
||||
},
|
||||
|
||||
// 分配账号给用户 (管理员)
|
||||
async assignAccount(req: Request, res: Response) {
|
||||
const { id } = req.params;
|
||||
const { userId, expiresAt } = req.body;
|
||||
|
||||
if (!id || !userId) {
|
||||
return res.status(400).json({ error: '账号ID和用户ID都是必需的' });
|
||||
}
|
||||
|
||||
// 检查账号是否存在
|
||||
const account = await prisma.websiteAccount.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return res.status(404).json({ error: '账号不存在' });
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: '用户不存在' });
|
||||
}
|
||||
|
||||
// 检查账号是否已满
|
||||
if (account.currentUsers >= account.maxUsers) {
|
||||
return res.status(400).json({ error: '账号已达到最大用户数' });
|
||||
}
|
||||
|
||||
// 检查是否已经分配
|
||||
const existingAssignment = await prisma.accountAssignment.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
accountId: id,
|
||||
isActive: true
|
||||
}
|
||||
});
|
||||
|
||||
if (existingAssignment) {
|
||||
return res.status(400).json({ error: '用户已分配此账号' });
|
||||
}
|
||||
|
||||
// 创建分配
|
||||
const assignment = await prisma.accountAssignment.create({
|
||||
data: {
|
||||
userId,
|
||||
accountId: id,
|
||||
expiresAt: expiresAt ? new Date(expiresAt) : null,
|
||||
isActive: true,
|
||||
}
|
||||
});
|
||||
|
||||
// 更新账号当前用户数
|
||||
await prisma.websiteAccount.update({
|
||||
where: { id },
|
||||
data: {
|
||||
currentUsers: {
|
||||
increment: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return res.json({
|
||||
message: '账号分配成功',
|
||||
assignment
|
||||
});
|
||||
},
|
||||
|
||||
// 取消账号分配 (管理员)
|
||||
async unassignAccount(req: Request, res: Response) {
|
||||
const { id, userId } = req.params;
|
||||
|
||||
if (!id || !userId) {
|
||||
return res.status(400).json({ error: '账号ID和用户ID都是必需的' });
|
||||
}
|
||||
|
||||
const assignment = await prisma.accountAssignment.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
accountId: id,
|
||||
isActive: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!assignment) {
|
||||
return res.status(404).json({ error: '分配不存在' });
|
||||
}
|
||||
|
||||
// 删除分配
|
||||
await prisma.accountAssignment.delete({
|
||||
where: { id: assignment.id }
|
||||
});
|
||||
|
||||
// 更新账号当前用户数
|
||||
await prisma.websiteAccount.update({
|
||||
where: { id },
|
||||
data: {
|
||||
currentUsers: {
|
||||
decrement: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return res.json({ message: '账号分配已取消' });
|
||||
},
|
||||
|
||||
// 网站登录
|
||||
async loginToWebsite(req: Request, res: Response) {
|
||||
const { accountId } = req.params;
|
||||
const { userId } = req.body;
|
||||
|
||||
if (!accountId || !userId) {
|
||||
return res.status(400).json({ error: '账号ID和用户ID是必需的' });
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取账号信息
|
||||
const account = await prisma.websiteAccount.findUnique({
|
||||
where: { id: accountId }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
return res.status(404).json({ error: '账号不存在' });
|
||||
}
|
||||
|
||||
// 检查用户是否有权限访问该账号
|
||||
console.log('检查用户权限:', { accountId, userId });
|
||||
|
||||
const assignment = await prisma.accountAssignment.findFirst({
|
||||
where: {
|
||||
accountId,
|
||||
userId,
|
||||
isActive: true,
|
||||
OR: [
|
||||
{ expiresAt: null },
|
||||
{ expiresAt: { gt: new Date() } }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
console.log('权限检查结果:', {
|
||||
assignment: assignment ? 'found' : 'not found',
|
||||
accountId,
|
||||
userId,
|
||||
currentTime: new Date()
|
||||
});
|
||||
|
||||
if (!assignment) {
|
||||
// 获取更多调试信息
|
||||
const allAssignments = await prisma.accountAssignment.findMany({
|
||||
where: { accountId, userId }
|
||||
});
|
||||
console.log('该用户的所有分配记录:', allAssignments);
|
||||
|
||||
return res.status(403).json({ error: '您没有权限访问该账号' });
|
||||
}
|
||||
|
||||
let loginUrl = '';
|
||||
|
||||
// 根据网站类型处理登录
|
||||
switch (account.website) {
|
||||
case 'claude':
|
||||
loginUrl = await handleClaudeLogin(account.token, userId);
|
||||
break;
|
||||
case 'chatgpt':
|
||||
loginUrl = await handleChatGPTLogin(account.token, userId);
|
||||
break;
|
||||
case 'grok':
|
||||
loginUrl = await handleGrokLogin(account.token, userId);
|
||||
break;
|
||||
default:
|
||||
return res.status(400).json({ error: '不支持的网站类型' });
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
loginUrl,
|
||||
website: account.website,
|
||||
accountName: account.accountName
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('网站登录失败:', error);
|
||||
return res.status(500).json({ error: '登录失败,请稍后重试' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Claude 登录处理
|
||||
async function handleClaudeLogin(token: string, userName: string): Promise<string> {
|
||||
try {
|
||||
const baseUrl = process.env.CLAUDE_TARGET_URL || 'https://chat.micar9.com:8443';
|
||||
console.log('Claude登录处理:', { token, userName });
|
||||
// 第一步:获取oauth token
|
||||
const oauthResponse = await fetch(`${baseUrl}/manage-api/auth/oauth_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: token,
|
||||
unique_name: userName
|
||||
})
|
||||
});
|
||||
|
||||
if (!oauthResponse.ok) {
|
||||
throw new Error(`OAuth token 请求失败: ${oauthResponse.status}`);
|
||||
}
|
||||
|
||||
const oauthData = await oauthResponse.json() as { login_url?: string };
|
||||
|
||||
if (!oauthData.login_url) {
|
||||
throw new Error('未获取到登录URL');
|
||||
}
|
||||
|
||||
return oauthData.login_url;
|
||||
} catch (error) {
|
||||
console.error('Claude登录处理失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ChatGPT 登录处理
|
||||
async function handleChatGPTLogin(token: string, userName: string): Promise<string> {
|
||||
try {
|
||||
const baseUrl = process.env.CLAUDE_TARGET_URL || 'http://127.0.0.1:8181';
|
||||
|
||||
const response = await fetch(`${baseUrl}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${process.env.ADMIN_PASSWORD || 'admin'}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
access_token: token,
|
||||
user_name: userName,
|
||||
isolated_session: true,
|
||||
limits: []
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`ChatGPT登录请求失败: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json() as { login_url?: string };
|
||||
|
||||
if (!data.login_url) {
|
||||
throw new Error('未获取到登录URL');
|
||||
}
|
||||
|
||||
return data.login_url;
|
||||
} catch (error) {
|
||||
console.error('ChatGPT登录处理失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Grok 登录处理
|
||||
async function handleGrokLogin(token: string, userName: string): Promise<string> {
|
||||
try {
|
||||
const baseUrl = process.env.CLAUDE_TARGET_URL || 'https://grok-mirror.micar9.com:8443';
|
||||
|
||||
const response = await fetch(`${baseUrl}/api/login-v2`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_name: userName,
|
||||
sso_token: token
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Grok登录请求失败: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json() as { login_url?: string };
|
||||
|
||||
if (!data.login_url) {
|
||||
throw new Error('未获取到登录URL');
|
||||
}
|
||||
|
||||
return data.login_url;
|
||||
} catch (error) {
|
||||
console.error('Grok登录处理失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user