349 lines
8.8 KiB
JavaScript
349 lines
8.8 KiB
JavaScript
const express = require('express');
|
||
const { body, validationResult } = require('express-validator');
|
||
const OAuthClient = require('../models/OAuthClient');
|
||
const { authenticateToken } = require('../middleware/auth');
|
||
|
||
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', 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; |