319 lines
6.9 KiB
JavaScript
319 lines
6.9 KiB
JavaScript
/**
|
||
* 用户路由
|
||
* 复用 server/src/routes/user.js 的逻辑
|
||
*/
|
||
|
||
import express from 'express';
|
||
import { prisma } from '../db/client.js';
|
||
import { encrypt } from '../db/crypto.js';
|
||
|
||
const router = express.Router();
|
||
|
||
// ==================== 用户设置 ====================
|
||
|
||
// 获取用户设置
|
||
router.get('/settings', async (req, res, next) => {
|
||
try {
|
||
let settings = await prisma.userSettings.findUnique({
|
||
where: { userId: req.user.id }
|
||
});
|
||
|
||
if (!settings) {
|
||
// 创建默认设置
|
||
settings = await prisma.userSettings.create({
|
||
data: { userId: req.user.id }
|
||
});
|
||
}
|
||
|
||
res.json(settings);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 更新用户设置
|
||
router.put('/settings', async (req, res, next) => {
|
||
try {
|
||
const settings = await prisma.userSettings.upsert({
|
||
where: { userId: req.user.id },
|
||
update: req.body,
|
||
create: {
|
||
userId: req.user.id,
|
||
...req.body
|
||
}
|
||
});
|
||
|
||
res.json(settings);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// ==================== API Keys 管理 ====================
|
||
|
||
// 获取 API Keys
|
||
router.get('/api-keys', async (req, res, next) => {
|
||
try {
|
||
const keys = await prisma.apiKey.findMany({
|
||
where: { userId: req.user.id },
|
||
orderBy: { order: 'asc' },
|
||
select: {
|
||
id: true,
|
||
provider: true,
|
||
remark: true,
|
||
status: true,
|
||
order: true,
|
||
lastUsedAt: true,
|
||
createdAt: true
|
||
// 不返回 keyValue (已加密)
|
||
}
|
||
});
|
||
|
||
res.json(keys);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 添加 API Key
|
||
router.post('/api-keys', async (req, res, next) => {
|
||
try {
|
||
const { provider, keyValue, remark, order } = req.body;
|
||
|
||
if (!provider || !keyValue) {
|
||
return res.status(400).json({ error: 'Provider and keyValue are required' });
|
||
}
|
||
|
||
// 加密 API Key
|
||
const encryptedKey = encrypt(keyValue);
|
||
|
||
const key = await prisma.apiKey.create({
|
||
data: {
|
||
userId: req.user.id,
|
||
provider,
|
||
keyValue: encryptedKey,
|
||
remark,
|
||
order: order || 0
|
||
}
|
||
});
|
||
|
||
res.status(201).json({
|
||
id: key.id,
|
||
provider: key.provider,
|
||
remark: key.remark,
|
||
status: key.status,
|
||
order: key.order
|
||
});
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 批量保存 API Keys(前端发送 { provider, keys: [...] })
|
||
router.post('/api-keys/batch', async (req, res, next) => {
|
||
try {
|
||
const { provider, keys } = req.body;
|
||
|
||
if (!provider || !Array.isArray(keys)) {
|
||
return res.status(400).json({ error: 'Provider and keys array are required' });
|
||
}
|
||
|
||
// 先删除该 provider 的所有旧 keys
|
||
await prisma.apiKey.deleteMany({
|
||
where: {
|
||
userId: req.user.id,
|
||
provider
|
||
}
|
||
});
|
||
|
||
// 批量创建新的 keys
|
||
const createdKeys = [];
|
||
for (let i = 0; i < keys.length; i++) {
|
||
const keyData = keys[i];
|
||
if (keyData.keyValue) {
|
||
const encryptedKey = encrypt(keyData.keyValue);
|
||
const key = await prisma.apiKey.create({
|
||
data: {
|
||
userId: req.user.id,
|
||
provider,
|
||
keyValue: encryptedKey,
|
||
remark: keyData.remark || '',
|
||
order: i
|
||
}
|
||
});
|
||
createdKeys.push({
|
||
id: key.id,
|
||
provider: key.provider,
|
||
remark: key.remark,
|
||
status: key.status,
|
||
order: key.order
|
||
});
|
||
}
|
||
}
|
||
|
||
res.status(201).json(createdKeys);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 更新 API Key 状态
|
||
router.patch('/api-keys/:id/status', async (req, res, next) => {
|
||
try {
|
||
const { status } = req.body;
|
||
|
||
if (!['VALID', 'INVALID', 'TESTING', 'UNTESTED'].includes(status)) {
|
||
return res.status(400).json({ error: 'Invalid status value' });
|
||
}
|
||
|
||
await prisma.apiKey.updateMany({
|
||
where: {
|
||
id: req.params.id,
|
||
userId: req.user.id
|
||
},
|
||
data: {
|
||
status,
|
||
lastUsedAt: status === 'VALID' ? new Date() : undefined
|
||
}
|
||
});
|
||
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 删除 API Key
|
||
router.delete('/api-keys/:id', async (req, res, next) => {
|
||
try {
|
||
await prisma.apiKey.deleteMany({
|
||
where: {
|
||
id: req.params.id,
|
||
userId: req.user.id
|
||
}
|
||
});
|
||
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// ==================== 已处理文件记录 ====================
|
||
|
||
// 获取已处理文件列表
|
||
router.get('/processed-files', async (req, res, next) => {
|
||
try {
|
||
const files = await prisma.processedFile.findMany({
|
||
where: { userId: req.user.id },
|
||
select: {
|
||
fileIdentifier: true,
|
||
fileName: true,
|
||
processedAt: true
|
||
}
|
||
});
|
||
|
||
res.json(files);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 标记文件为已处理
|
||
router.post('/processed-files', async (req, res, next) => {
|
||
try {
|
||
const { fileIdentifier, fileName } = req.body;
|
||
|
||
if (!fileIdentifier || !fileName) {
|
||
return res.status(400).json({ error: 'fileIdentifier and fileName are required' });
|
||
}
|
||
|
||
const file = await prisma.processedFile.upsert({
|
||
where: {
|
||
userId_fileIdentifier: {
|
||
userId: req.user.id,
|
||
fileIdentifier
|
||
}
|
||
},
|
||
update: {
|
||
fileName,
|
||
processedAt: new Date()
|
||
},
|
||
create: {
|
||
userId: req.user.id,
|
||
fileIdentifier,
|
||
fileName
|
||
}
|
||
});
|
||
|
||
res.status(201).json(file);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 检查文件是否已处理
|
||
router.get('/processed-files/check/:identifier', async (req, res, next) => {
|
||
try {
|
||
const file = await prisma.processedFile.findUnique({
|
||
where: {
|
||
userId_fileIdentifier: {
|
||
userId: req.user.id,
|
||
fileIdentifier: req.params.identifier
|
||
}
|
||
}
|
||
});
|
||
|
||
res.json({ processed: !!file });
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 批量检查文件是否已处理
|
||
router.post('/processed-files/check-batch', async (req, res, next) => {
|
||
try {
|
||
const { identifiers } = req.body;
|
||
|
||
if (!Array.isArray(identifiers)) {
|
||
return res.status(400).json({ error: 'identifiers must be an array' });
|
||
}
|
||
|
||
const files = await prisma.processedFile.findMany({
|
||
where: {
|
||
userId: req.user.id,
|
||
fileIdentifier: {
|
||
in: identifiers
|
||
}
|
||
},
|
||
select: {
|
||
fileIdentifier: true
|
||
}
|
||
});
|
||
|
||
const processedSet = new Set(files.map(f => f.fileIdentifier));
|
||
const result = {};
|
||
identifiers.forEach(id => {
|
||
result[id] = processedSet.has(id);
|
||
});
|
||
|
||
res.json(result);
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
// 清空已处理文件记录
|
||
router.delete('/processed-files', async (req, res, next) => {
|
||
try {
|
||
await prisma.processedFile.deleteMany({
|
||
where: { userId: req.user.id }
|
||
});
|
||
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
next(error);
|
||
}
|
||
});
|
||
|
||
export default router; |