const express = require('express'); const { body, validationResult } = require('express-validator'); const OAuthClient = require('../models/OAuthClient'); const { authenticateToken } = require('../middleware/auth'); const { oauthClientLimiter } = require('../middleware/rateLimit'); const router = express.Router(); // 验证中间件 const handleValidationErrors = (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ success: false, message: '输入验证失败', errors: errors.array().map(error => ({ field: error.path, message: error.msg })) }); } next(); }; // 自定义URL验证函数 const isValidRedirectUri = (value) => { console.log('验证URL:', value); try { const url = new URL(value); console.log('URL解析结果:', { protocol: url.protocol, hostname: url.hostname, pathname: url.pathname, isValid: (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname && url.pathname }); // 允许http和https协议,包括localhost return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname && url.pathname; } catch (error) { console.log('URL验证失败:', value, error.message); return false; } }; // 创建OAuth客户端验证规则 const createClientValidation = [ body('name') .isLength({ min: 1, max: 100 }) .withMessage('应用名称长度必须在1-100个字符之间'), body('description') .optional() .isLength({ max: 500 }) .withMessage('描述长度不能超过500个字符'), body('redirect_uris') .isArray({ min: 1 }) .withMessage('至少需要一个重定向URI'), body('scopes') .optional() .isArray() .withMessage('权限范围必须是数组格式') ]; // 自定义验证中间件 const validateRedirectUris = (req, res, next) => { const { redirect_uris } = req.body; if (!Array.isArray(redirect_uris)) { return res.status(400).json({ success: false, message: 'redirect_uris必须是数组' }); } for (let i = 0; i < redirect_uris.length; i++) { if (!isValidRedirectUri(redirect_uris[i])) { return res.status(400).json({ success: false, message: '输入验证失败', errors: [{ field: `redirect_uris[${i}]`, message: '重定向URI必须是有效的URL' }] }); } } next(); }; // 1. 创建OAuth客户端 router.post('/clients', oauthClientLimiter, authenticateToken, createClientValidation, validateRedirectUris, handleValidationErrors, async (req, res) => { try { const { name, description, redirect_uris, scopes } = req.body; const userId = req.user.userId; const clientData = { name, description: description || '', redirectUris: redirect_uris, scopes: scopes || ['read', 'write'], userId }; const newClient = await OAuthClient.create(clientData); res.status(201).json({ success: true, message: 'OAuth客户端创建成功', data: { client_id: newClient.client_id, client_secret: newClient.client_secret, // 返回客户端密钥 name: newClient.name, description: newClient.description, redirect_uris: newClient.redirect_uris, scopes: newClient.scopes, created_at: newClient.created_at } }); } catch (error) { console.error('创建OAuth客户端失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 2. 获取用户的所有OAuth客户端 router.get('/clients', authenticateToken, async (req, res) => { try { const userId = req.user.userId; const clients = await OAuthClient.findByUserId(userId); res.json({ success: true, data: { clients: clients.map(client => ({ client_id: client.client_id, name: client.name, description: client.description, redirect_uris: client.redirect_uris, scopes: client.scopes, created_at: client.created_at })) } }); } catch (error) { console.error('获取OAuth客户端失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 3. 获取特定OAuth客户端详情 router.get('/clients/:clientId', authenticateToken, async (req, res) => { try { const { clientId } = req.params; const userId = req.user.userId; const client = await OAuthClient.findByClientId(clientId); if (!client) { return res.status(404).json({ success: false, message: 'OAuth客户端不存在' }); } // 检查是否是客户端所有者 if (client.user_id !== userId) { return res.status(403).json({ success: false, message: '无权访问此客户端' }); } res.json({ success: true, data: { client_id: client.client_id, name: client.name, description: client.description, redirect_uris: client.redirect_uris, scopes: client.scopes, created_at: client.created_at } }); } catch (error) { console.error('获取OAuth客户端详情失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 4. 删除OAuth客户端 router.delete('/clients/:clientId', authenticateToken, async (req, res) => { try { const { clientId } = req.params; const userId = req.user.userId; const deleted = await OAuthClient.delete(clientId, userId); if (!deleted) { return res.status(404).json({ success: false, message: 'OAuth客户端不存在或无权删除' }); } res.json({ success: true, message: 'OAuth客户端已删除' }); } catch (error) { console.error('删除OAuth客户端失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 4. 获取客户端密钥(仅限客户端所有者) router.get('/clients/:clientId/secret', authenticateToken, async (req, res) => { try { const { clientId } = req.params; const userId = req.user.userId; const client = await OAuthClient.findByClientId(clientId); if (!client) { return res.status(404).json({ success: false, message: 'OAuth客户端不存在' }); } // 检查是否是客户端所有者 if (client.user_id !== userId) { return res.status(403).json({ success: false, message: '无权访问此客户端' }); } res.json({ success: true, data: { client_id: client.client_id, client_secret: client.client_secret } }); } catch (error) { console.error('获取客户端密钥失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 5. 重置客户端密钥 router.post('/clients/:clientId/reset-secret', authenticateToken, async (req, res) => { try { const { clientId } = req.params; const userId = req.user.userId; const client = await OAuthClient.findByClientId(clientId); if (!client) { return res.status(404).json({ success: false, message: 'OAuth客户端不存在' }); } // 检查是否是客户端所有者 if (client.user_id !== userId) { return res.status(403).json({ success: false, message: '无权操作此客户端' }); } const newSecret = await OAuthClient.resetClientSecret(clientId); res.json({ success: true, message: '客户端密钥重置成功', data: { client_id: clientId, client_secret: newSecret } }); } catch (error) { console.error('重置客户端密钥失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); // 5. 获取OAuth客户端统计信息 router.get('/clients/:clientId/stats', authenticateToken, async (req, res) => { try { const { clientId } = req.params; const userId = req.user.userId; const client = await OAuthClient.findByClientId(clientId); if (!client || client.user_id !== userId) { return res.status(404).json({ success: false, message: 'OAuth客户端不存在' }); } // 这里可以添加更多统计信息,比如活跃令牌数量等 res.json({ success: true, data: { client_id: client.client_id, name: client.name, created_at: client.created_at, // 可以添加更多统计信息 } }); } catch (error) { console.error('获取OAuth客户端统计失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); module.exports = router;