first commit

This commit is contained in:
2025-07-08 00:52:10 +08:00
commit aa2416c5d6
69 changed files with 16628 additions and 0 deletions

View 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;
}
}