/** * 用户路由 * 复用 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;