import express from 'express'; import { query } from '../config.js'; import { asyncHandler } from '../middleware/errorHandler.js'; import { requireAuth, requireSuperAdmin } from '../middleware/auth.js'; import AuditLog from '../models/AuditLog.js'; const router = express.Router(); /** * GET /api/llm-config * 取得當前 LLM 配置(所有使用者可見) */ router.get('/', requireAuth, asyncHandler(async (req, res) => { const configs = await query( `SELECT id, provider, api_url, model_name, is_active, created_at, updated_at FROM llm_configs ORDER BY is_active DESC, created_at DESC` ); res.json({ success: true, data: configs }); })); /** * GET /api/llm-config/active * 取得當前啟用的 LLM 配置 */ router.get('/active', requireAuth, asyncHandler(async (req, res) => { const [config] = await query( `SELECT id, provider, api_url, model_name, temperature, max_tokens, timeout FROM llm_configs WHERE is_active = 1 LIMIT 1` ); if (!config) { return res.status(404).json({ success: false, error: '未找到啟用的 LLM 配置' }); } res.json({ success: true, data: config }); })); /** * POST /api/llm-config * 新增 LLM 配置(僅管理員) */ router.post('/', requireSuperAdmin, asyncHandler(async (req, res) => { const { provider, api_url, api_key, model_name, temperature, max_tokens, timeout } = req.body; // 驗證必填欄位 if (!provider || !api_url || !model_name) { return res.status(400).json({ success: false, error: '請填寫所有必填欄位' }); } const result = await query( `INSERT INTO llm_configs (provider, api_url, api_key, model_name, temperature, max_tokens, timeout) VALUES (?, ?, ?, ?, ?, ?, ?)`, [ provider, api_url, api_key || null, model_name, temperature || 0.7, max_tokens || 6000, timeout || 120000 ] ); // 記錄稽核日誌 await AuditLog.logCreate( req.session.userId, 'llm_config', result.insertId, { provider, model_name }, req.ip, req.get('user-agent') ); res.json({ success: true, message: '已新增 LLM 配置', data: { id: result.insertId } }); })); /** * PUT /api/llm-config/:id * 更新 LLM 配置(僅管理員) */ router.put('/:id', requireSuperAdmin, asyncHandler(async (req, res) => { const configId = parseInt(req.params.id); const { provider, api_url, api_key, model_name, temperature, max_tokens, timeout } = req.body; // 驗證必填欄位 if (!provider || !api_url || !model_name) { return res.status(400).json({ success: false, error: '請填寫所有必填欄位' }); } // 檢查配置是否存在 const [existing] = await query('SELECT id FROM llm_configs WHERE id = ?', [configId]); if (!existing) { return res.status(404).json({ success: false, error: '找不到此 LLM 配置' }); } await query( `UPDATE llm_configs SET provider = ?, api_url = ?, api_key = ?, model_name = ?, temperature = ?, max_tokens = ?, timeout = ?, updated_at = NOW() WHERE id = ?`, [ provider, api_url, api_key || null, model_name, temperature || 0.7, max_tokens || 6000, timeout || 120000, configId ] ); // 記錄稽核日誌 await AuditLog.logUpdate( req.session.userId, 'llm_config', configId, {}, { provider, model_name }, req.ip, req.get('user-agent') ); res.json({ success: true, message: '已更新 LLM 配置' }); })); /** * PUT /api/llm-config/:id/activate * 啟用特定 LLM 配置(僅管理員) */ router.put('/:id/activate', requireSuperAdmin, asyncHandler(async (req, res) => { const configId = parseInt(req.params.id); // 檢查配置是否存在 const [existing] = await query('SELECT id, provider FROM llm_configs WHERE id = ?', [configId]); if (!existing) { return res.status(404).json({ success: false, error: '找不到此 LLM 配置' }); } // 先停用所有配置 await query('UPDATE llm_configs SET is_active = 0'); // 啟用指定配置 await query('UPDATE llm_configs SET is_active = 1, updated_at = NOW() WHERE id = ?', [configId]); // 記錄稽核日誌 await AuditLog.logUpdate( req.session.userId, 'llm_config', configId, { is_active: 0 }, { is_active: 1 }, req.ip, req.get('user-agent') ); res.json({ success: true, message: `已啟用 ${existing.provider} 配置` }); })); /** * DELETE /api/llm-config/:id * 刪除 LLM 配置(僅管理員) */ router.delete('/:id', requireSuperAdmin, asyncHandler(async (req, res) => { const configId = parseInt(req.params.id); // 檢查是否為啟用中的配置 const [existing] = await query('SELECT is_active FROM llm_configs WHERE id = ?', [configId]); if (!existing) { return res.status(404).json({ success: false, error: '找不到此 LLM 配置' }); } if (existing.is_active) { return res.status(400).json({ success: false, error: '無法刪除啟用中的配置' }); } await query('DELETE FROM llm_configs WHERE id = ?', [configId]); // 記錄稽核日誌 await AuditLog.logDelete( req.session.userId, 'llm_config', configId, {}, req.ip, req.get('user-agent') ); res.json({ success: true, message: '已刪除 LLM 配置' }); })); /** * POST /api/llm-config/test * 測試 LLM 配置連線(僅管理員) */ router.post('/test', requireSuperAdmin, asyncHandler(async (req, res) => { const { api_url, api_key, model_name } = req.body; if (!api_url || !model_name) { return res.status(400).json({ success: false, error: '請提供 API 端點和模型名稱' }); } try { const axios = (await import('axios')).default; const response = await axios.post( `${api_url}/v1/chat/completions`, { model: model_name, messages: [ { role: 'user', content: 'Hello' } ], max_tokens: 10 }, { timeout: 10000, headers: { 'Content-Type': 'application/json', ...(api_key && { 'Authorization': `Bearer ${api_key}` }) } } ); if (response.data && response.data.choices) { res.json({ success: true, message: 'LLM API 連線測試成功' }); } else { throw new Error('Invalid API response format'); } } catch (error) { res.status(500).json({ success: false, error: 'LLM API 連線測試失敗', message: error.message }); } })); export default router;