安全加固 + 结构清理:修注入/串写/竞态,DOMPurify 上线,后端补事务与 handler 测试

后端:
- ORDER BY 白名单(sanitizeOrder)堵住 ?order= SQL 注入,补回归测试
- 登录限速(每 IP 10 次失败/10 分钟 429)、TLS/反代下 Secure cookie、NewAPI 构造器
- Delete/setTags/MergeTags/DeleteTag 包事务;Archive 去 500 篇上限
- 列表接口裁剪:不传 content_md,长文 content_html 截 600,新增 content_len;health 探 DB

前端:
- EditorView 路由复用串写修复(RouterView :key + sync watch 回写原文章)
- v-html 出口统一过 DOMPurify(sanitizeHtml),stripTags 改 DOMParser
- 列表竞态防护(Home/Tag/Posts 请求序号)、TagView 分页修复
- 侧栏接口 30s 缓存去重;one:unauthorized 监听器泄漏修复
- 删 styles.css 498 行重复块;移除 tailwind/marked/vue-tsc 死依赖;CommandPalette a11y 语义
This commit is contained in:
Sakurasan
2026-09-21 23:59:09 +08:00
parent c762f06cd7
commit dd2994189a
25 changed files with 539 additions and 1411 deletions
+6 -3
View File
@@ -38,10 +38,12 @@ onMounted(async () => {
})
// 会话过期时(请求返回 401)统一跳回登录页
window.addEventListener('one:unauthorized', () => {
function onUnauthorized() {
session.clear()
if (router.currentRoute.value.path.startsWith('/admin')) router.replace('/admin/login')
})
}
onMounted(() => window.addEventListener('one:unauthorized', onUnauthorized))
onBeforeUnmount(() => window.removeEventListener('one:unauthorized', onUnauthorized))
async function logout() {
try {
@@ -149,7 +151,8 @@ watch(() => route.path, () => loadCounts())
</div>
</header>
<main class="admin-content">
<RouterView />
<!-- 编辑器在 /admin/:id 间切换会复用组件:按 id 重建实例,防止串写 -->
<RouterView :key="route.name === 'admin-edit' ? route.params.id : undefined" />
</main>
</div>