const express = require('express'); const router = express.Router(); const {Article} = require('../../models') const {Op} = require('sequelize') /* 查询文章列表 GET /admin/articles */ router.get('/', async function(req, res, next) { try { const query = req.query //当前是第几页,如果不传,那就是第一页 const currentPage = Math.abs(Number(query.currentPage)) || 1 //每页显示多少条数据,如果不传,那就显示10条 const pageSize = Math.abs(Number(query.pageSize)) || 10 //计算 offset const offset = (currentPage - 1) * pageSize const condition = { order:[['id','DESC']], limit:pageSize, offset } if(query.title){ condition.where = { title:{ [Op.like]:`%${query.title}%` } } } const {count ,rows} = await Article.findAndCountAll(condition) res.json({ status:true, message:'成功', data:{ articles:rows, pagination:{ total:count, currentPage, pageSize } } }); }catch(error){ res.status(500).json({ status:false, message:'失败', errors:[error.message] }); } }); /* 查询文章详情 GET /admin/articles/:id */ router.get('/:id', async function(req, res, next) { try { //获取文章id const {id} = req.params //查询文章 const article = await Article.findByPk(id) if(article){ res.json({ status:true, message:'成功', data:article }); }else{ res.status(404).json({ status:false, message:'文章未找到', }); } }catch(error){ res.status(500).json({ status:false, message:'失败', errors:[error.message] }); } }); /* 创建文章 POST /admin/articles/ */ 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('过滤后的数据:', { 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:'成功', data:article }); }catch(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.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:['数据库操作失败,请稍后重试'] }); }else if(error.name === 'SequelizeConnectionError'){ res.status(500).json({ status:false, message:'数据库连接错误', errors:['数据库连接失败,请稍后重试'] }); }else{ res.status(500).json({ status:false, message:'服务器内部错误', errors:['服务器处理请求时发生错误,请稍后重试'] }); } } }); /* 删除文章 GET /admin/articles/:id */ router.delete('/:id', async function(req, res, next) { try { //获取文章id const {id} = req.params //查询文章 const article = await Article.findByPk(id) if(article){ await article.destroy() res.json({ status:true, message:'成功', }); }else{ res.status(404).json({ status:false, message:'文章未找到', }); } }catch(error){ res.status(500).json({ status:false, message:'失败', errors:[error.message] }); } }); /* 更新文章 GET /admin/articles/:id */ router.put('/:id', async function(req, res, next) { try { //获取文章id const {id} = req.params //查询文章 const article = await Article.findByPk(id) //白名单过滤 const body = filterBody(req) if(article){ await article.update(body) res.json({ status:true, message:'成功', data:article }); }else{ res.status(404).json({ status:false, message:'文章未找到', }); } }catch(error){ res.status(500).json({ status:false, message:'失败', errors:[error.message] }); } }); function filterBody(req){ 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.title) { throw new Error('标题不能为空'); } if (!body.content) { throw new Error('内容不能为空'); } // 验证标题长度 - 放宽限制以适应富文本编辑器 if (body.title.length > 500) { throw new Error('标题长度不能超过500个字符'); } // 验证内容长度 - 防止过大的内容 if (body.content.length > 5000000) { // 5MB限制 throw new Error('内容过长,请减少内容长度'); } // 检查富文本内容是否包含危险标签或脚本 const dangerousTags = /]*>.*?<\/script>/gi; if (dangerousTags.test(body.content)) { throw new Error('内容包含不允许的脚本标签'); } return body; } catch (error) { console.error('filterBody错误:', error); throw error; } } module.exports = router;