feat: v1.3.0 差异化重构——星图点亮体系

P0 修复:
- 详情页正文渲染(content 分段),71 条知识不再空白
- 问答页去登录墙:游客每日 3 次(IP 限流),登录后每日 5 次
- 修正问答配额为真正的每日重置语义

点亮闭环:
- KnowledgeLit 模型 + 服务端判题(POST /:id/quiz,答案不下发)
- /api/knowledge/stats 星域统计 + 点亮进度 + streak + litIds
- AI 批量生成 71 条读后检测题(scripts/generate-quiz.js)

设计系统 2.0:
- 70 个统一线性图标替换全部 emoji 图标
- 主题 token mixin 化,修复 H5 暗色级联 bug,MP 跟随系统(darkmode)
- 首页星域导航(进度环)、我的页学习者档案、星域列表页重写
- 硬编码色清理,误植 map 页移除

服务端加固:
- trust proxy 真实 IP、错误信息脱敏、ask 独立限流、无效 ObjectId 404
- seo.js ESM→CJS(修复容器启动)、AI 导师"维度精灵"人格化
This commit is contained in:
2026-09-12 23:39:05 +08:00
parent cbc1a587f2
commit 2a111d21c0
104 changed files with 2776 additions and 2109 deletions
+19 -9
View File
@@ -15,6 +15,9 @@ const logger = require('./utils/logger')
// 创建 Express 应用
const app = express()
// 信任反向代理(nginx/宝塔),使 req.ip 取到真实客户端 IP
app.set('trust proxy', 1)
// 数据库连接
connectDB()
@@ -22,11 +25,14 @@ connectDB()
app.use('/', seoRoutes)
// 中间件配置
app.use(helmet()) // 安全头部
app.use(helmet({
contentSecurityPolicy: false, // H5 与小程序客户端各自管理 CSP;API 服务无需开启
crossOriginResourcePolicy: { policy: 'cross-origin' }
})) // 安全头部
app.use(cors()) // 跨域支持
app.use(compression()) // Gzip 压缩
app.use(express.json({ limit: '10mb' })) // JSON 解析
app.use(express.urlencoded({ extended: true, limit: '10mb' })) // URL 编码解析
app.use(express.json({ limit: '1mb' })) // JSON 解析(普通接口无需 10mb 大包)
app.use(express.urlencoded({ extended: true, limit: '1mb' })) // URL 编码解析
// 日志中间件
if (process.env.NODE_ENV === 'development') {
@@ -67,16 +73,20 @@ app.use((req, res) => {
})
})
// 全局错误处理
// 全局错误处理(不向客户端泄露内部错误细节)
app.use((err, req, res, next) => {
logger.error('服务器错误:', err)
// JSON 解析错误等客户端问题返回 400
if (err.type === 'entity.parse.failed' || err.type === 'entity.too.large') {
return res.status(400).json({ success: false, error: '请求体格式错误' })
}
res.status(err.status || 500).json({
success: false,
error: process.env.NODE_ENV === 'production'
? '服务器内部错误'
: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
error: process.env.NODE_ENV === 'production'
? '服务器内部错误'
: err.message
})
})