fix(blog): tags 双重编码导致详情页 500 + 上传文件名去中文

问题:
- 管理后台编辑博客保存后,访问 /blog/{slug} 返回 500
- 用户上传封面图后前端不识别/裂图

根因:
1. admin [id].get.js 原样返回 DB 里的 tags 字符串 "[]",编辑页 loadItem
   透传到 form.tags,保存时 JSON.stringify 再编码一次 → DB 写入 '"[]"'。
   公开接口 JSON.parse 后得到字符串 "[]" 而非数组,前端 tags.join(',')
   对字符串调用 → TypeError → SSR 500。
2. 上传接口保留中文文件名(\u4e00-\u9fa5 正则放行),Nginx/Nuxt 静态服务
   对含中文 URL 处理不一致,静态请求 404。

修复:
- admin GET:tags 统一解析为数组返回
- admin POST/PUT:只接受数组,非数组写入空数组
- 公开 /api/content/blog|news:safeParseTags 兼容历史脏数据,
  即使 DB 里是 '"[]"' / 非法 JSON 也保证输出数组
- upload.post.js:safeName 只保留 ASCII 字母数字,去除中文
- scripts/fix-articles-tags.mjs:一次性清理历史双重编码数据
这个提交包含在:
wx
2026-04-20 13:21:04 +08:00
父节点 f91ecbb093
当前提交 61c94b741e
共修改 6 个文件,包含 78 行新增和 8 行删除
+14 -2
查看文件
@@ -1,6 +1,18 @@
import { eq, desc, asc } from 'drizzle-orm'
import { useDB, schema } from '../../utils/db.js'
// 防御性解析 tags:兼容历史脏数据(字符串字面量 "[]"、null、非法 JSON),始终返回数组
function safeParseTags(raw) {
if (!raw) return []
try {
let v = JSON.parse(raw)
if (typeof v === 'string') {
try { v = JSON.parse(v) } catch { return [] }
}
return Array.isArray(v) ? v : []
} catch { return [] }
}
/**
* 实体表数据构建器
* 当 key 对应独立实体表时,从实体表动态读取数据,
@@ -121,7 +133,7 @@ function buildFromEntityTable(db, key, siteData) {
category: a.category,
coverImage: a.coverImage,
author: a.author,
tags: a.tags ? JSON.parse(a.tags) : [],
tags: safeParseTags(a.tags),
date: a.createdAt,
}))
return { articles: items }
@@ -141,7 +153,7 @@ function buildFromEntityTable(db, key, siteData) {
category: a.category,
coverImage: a.coverImage,
author: a.author,
tags: a.tags ? JSON.parse(a.tags) : [],
tags: safeParseTags(a.tags),
seoTitle: a.seoTitle,
seoDescription: a.seoDesc,
seoKeywords: a.seoKeywords,