|
@@ -96,14 +96,27 @@ router.get('/:id', async function(req, res, next) {
|
|
|
*/
|
|
|
router.post('/', async function(req, res, next) {
|
|
|
try {
|
|
|
+ // 添加请求日志
|
|
|
+ console.log('=== 创建文章请求开始 ===');
|
|
|
+ console.log('请求体大小:', JSON.stringify(req.body).length);
|
|
|
+ console.log('Content字段长度:', req.body.content ? req.body.content.length : 0);
|
|
|
+ console.log('Title字段长度:', req.body.title ? req.body.title.length : 0);
|
|
|
+
|
|
|
//白名单过滤
|
|
|
const body = filterBody(req)
|
|
|
|
|
|
- // 添加调试日志
|
|
|
- console.log('创建文章请求体大小:', JSON.stringify(body).length);
|
|
|
- console.log('Content字段长度:', body.content ? body.content.length : 0);
|
|
|
+ console.log('过滤后的数据:', {
|
|
|
+ titleLength: body.title ? body.title.length : 0,
|
|
|
+ contentLength: body.content ? body.content.length : 0,
|
|
|
+ hasImage: !!body.img,
|
|
|
+ type: body.type
|
|
|
+ });
|
|
|
|
|
|
const article = await Article.create(body)
|
|
|
+
|
|
|
+ console.log('文章创建成功, ID:', article.id);
|
|
|
+ console.log('=== 创建文章请求结束 ===');
|
|
|
+
|
|
|
res.status(201).json({
|
|
|
status:true,
|
|
|
message:'成功',
|
|
@@ -111,35 +124,45 @@ router.post('/', async function(req, res, next) {
|
|
|
});
|
|
|
}catch(error){
|
|
|
// 添加详细的错误日志
|
|
|
- console.error('创建文章错误:', error);
|
|
|
+ console.error('=== 创建文章错误 ===');
|
|
|
+ console.error('错误名称:', error.name);
|
|
|
+ console.error('错误消息:', error.message);
|
|
|
console.error('错误堆栈:', error.stack);
|
|
|
+ console.error('请求体大小:', JSON.stringify(req.body).length);
|
|
|
console.error('请求体:', JSON.stringify(req.body, null, 2));
|
|
|
|
|
|
- if(error.name === 'SequelizeValidationError'){
|
|
|
- const errors = error.errors.map(e =>e.message)
|
|
|
-
|
|
|
+ if(error.message === '标题不能为空' || error.message === '内容不能为空' ||
|
|
|
+ error.message.includes('长度不能超过') || error.message.includes('不允许的脚本标签')){
|
|
|
res.status(400).json({
|
|
|
status:false,
|
|
|
message:'请求参数错误',
|
|
|
+ errors:[error.message]
|
|
|
+ });
|
|
|
+ }else if(error.name === 'SequelizeValidationError'){
|
|
|
+ const errors = error.errors.map(e => e.message)
|
|
|
+ res.status(400).json({
|
|
|
+ status:false,
|
|
|
+ message:'数据验证失败',
|
|
|
errors
|
|
|
});
|
|
|
}else if(error.name === 'SequelizeDatabaseError'){
|
|
|
+ console.error('数据库错误详情:', error.original);
|
|
|
res.status(500).json({
|
|
|
status:false,
|
|
|
message:'数据库错误',
|
|
|
- errors:[error.message]
|
|
|
+ errors:['数据库操作失败,请稍后重试']
|
|
|
});
|
|
|
}else if(error.name === 'SequelizeConnectionError'){
|
|
|
res.status(500).json({
|
|
|
status:false,
|
|
|
message:'数据库连接错误',
|
|
|
- errors:[error.message]
|
|
|
+ errors:['数据库连接失败,请稍后重试']
|
|
|
});
|
|
|
}else{
|
|
|
res.status(500).json({
|
|
|
status:false,
|
|
|
message:'服务器内部错误',
|
|
|
- errors:[error.message]
|
|
|
+ errors:['服务器处理请求时发生错误,请稍后重试']
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -219,35 +242,51 @@ router.put('/:id', async function(req, res, next) {
|
|
|
});
|
|
|
|
|
|
function filterBody(req){
|
|
|
- // 数据清理和验证
|
|
|
- const body = {
|
|
|
- title: req.body.title ? String(req.body.title).trim() : null,
|
|
|
- content: req.body.content ? String(req.body.content) : null,
|
|
|
- type: req.body.type ? parseInt(req.body.type) : null,
|
|
|
- img: req.body.img ? String(req.body.img).trim() : null,
|
|
|
- date: req.body.date ? new Date(req.body.date) : null,
|
|
|
- author: req.body.author ? String(req.body.author).trim() : null,
|
|
|
- category: req.body.category ? parseInt(req.body.category) : null,
|
|
|
- crop: req.body.crop ? parseInt(req.body.crop) : null,
|
|
|
- seoKeyword: req.body.seoKeyword ? String(req.body.seoKeyword).trim() : null,
|
|
|
- seoDescription: req.body.seoDescription ? String(req.body.seoDescription).trim() : null
|
|
|
- };
|
|
|
-
|
|
|
- // 验证必填字段
|
|
|
- if (!body.title) {
|
|
|
- throw new Error('标题不能为空');
|
|
|
- }
|
|
|
+ try {
|
|
|
+ // 数据清理和验证
|
|
|
+ const body = {
|
|
|
+ title: req.body.title ? String(req.body.title).trim() : null,
|
|
|
+ content: req.body.content ? String(req.body.content) : null,
|
|
|
+ type: req.body.type ? parseInt(req.body.type) : null,
|
|
|
+ img: req.body.img ? String(req.body.img).trim() : null,
|
|
|
+ date: req.body.date ? new Date(req.body.date) : null,
|
|
|
+ author: req.body.author ? String(req.body.author).trim() : null,
|
|
|
+ category: req.body.category ? parseInt(req.body.category) : null,
|
|
|
+ crop: req.body.crop ? parseInt(req.body.crop) : null,
|
|
|
+ seoKeyword: req.body.seoKeyword ? String(req.body.seoKeyword).trim() : null,
|
|
|
+ seoDescription: req.body.seoDescription ? String(req.body.seoDescription).trim() : null
|
|
|
+ };
|
|
|
|
|
|
- if (!body.content) {
|
|
|
- throw new Error('内容不能为空');
|
|
|
- }
|
|
|
+ // 验证必填字段
|
|
|
+ if (!body.title) {
|
|
|
+ throw new Error('标题不能为空');
|
|
|
+ }
|
|
|
|
|
|
- // 验证标题长度
|
|
|
- if (body.title.length > 255) {
|
|
|
- throw new Error('标题长度不能超过255个字符');
|
|
|
- }
|
|
|
+ if (!body.content) {
|
|
|
+ throw new Error('内容不能为空');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证标题长度 - 放宽限制以适应富文本编辑器
|
|
|
+ if (body.title.length > 500) {
|
|
|
+ throw new Error('标题长度不能超过500个字符');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证内容长度 - 防止过大的内容
|
|
|
+ if (body.content.length > 5000000) { // 5MB限制
|
|
|
+ throw new Error('内容过长,请减少内容长度');
|
|
|
+ }
|
|
|
|
|
|
- return body;
|
|
|
+ // 检查富文本内容是否包含危险标签或脚本
|
|
|
+ const dangerousTags = /<script[^>]*>.*?<\/script>/gi;
|
|
|
+ if (dangerousTags.test(body.content)) {
|
|
|
+ throw new Error('内容包含不允许的脚本标签');
|
|
|
+ }
|
|
|
+
|
|
|
+ return body;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('filterBody错误:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-module.exports = router;
|
|
|
+module.exports = router;
|