背景
对全仓库(~44k LOC,184 个 Rust 文件)做的深度代码审查,覆盖两条主线:①代码质量 (功能 bug / 安全 / 性能 / 一致性)与 ②死代码/冗余 (整模块死代码、系统性重复、死分支)。
方法:编译器权威 dead-code 扫描(--force-warn dead_code)+ 4 路并行深度审查(API / MCP+Infra / 前端 / 跨模块去重),关键发现均已回源码核对。仅记录编译器无法发现或漏报的项;不包含 Dioxus #[server] 函数的 unreachable_pub 噪音(HTTP 调用,编译器看不到调用图)。
统计 :严重 5 / 中等 12 / 死代码 12 / 系统性重复 6 / 轻微与文档漂移 ~20 / 重复常量 10+。下方 checkbox 可逐项勾选追踪。
一、🔴 严重(CRITICAL)— 功能 Bug / 安全 / 进程级风险
二、🟠 中等(MODERATE)— 性能 / 一致性 / 数据正确性
M1 · MCP trash/delete 缺 author 归属校验 — src/mcp/tools/posts.rs:~437,~499
trash_post/delete_post 用 WHERE id = $1 …,无 author_id 过滤;而同文件 update_post/publish_post 都有 WHERE author_id = $2。任意 write-scope token 可删除/废弃他人文章。鉴权同文件内不一致。
M2 · 标签文章数 bug(api + mcp 双重镜像) — src/api/posts/tags.rs:33 + src/mcp/tools/read.rs:~290
COUNT(pt.post_id) 数了草稿/已删文章(posts JOIN 在 ON 子句里,count 没用 p.id)。应改 COUNT(p.id)。影响公开标签云(缓存 300s)和 MCP list_tags。已是重复代码漂移的实例 (见 R1)。
M3 · backup.rs 在 async fn 里阻塞 tokio worker — src/api/database/backup.rs:167-190,444-456
std::process::Command + child.wait_with_output() 跑 pg_dump/psql(可能数十秒~分钟),违反 spawn_blocking 约定。L368 read_to_string 把整个备份文件读进内存只为验首行签名;L497-512 list_backups 对每个 .sql 文件全量读入只为取第三行 -- mode:。
修复 :包 spawn_blocking(或 tokio::process/tokio::fs),读首行用 BufReader。
M4 · 评论 markdown 渲染未走 spawn_blocking — src/api/comments/create.rs:148
render_comment_markdown(pulldown + syntect + KaTeX,CPU 密集)直接跑在 async runtime。posts/* 都包了 spawn_blocking。评论里的代码块是攻击者可控的 CPU 负载。
M5 · 回收站批量操作漏 SSR 失效 — src/api/posts/trash.rs:347-354,412-419
batch_purge_posts/empty_trash 只调 bump_global_generation(),漏调 invalidate_ssr_all_public()(单条 purge/restore 都调了)。批删/清空回收站后 SSR 缓存的列表/归档页仍显示已删文章。bump_global_generation 自身注释也说"当前不会使 SSR 缓存失效"。
M6 · MCP publish_post 漏标签缓存失效 — src/mcp/tools/posts.rs:~405
漏 cache::invalidate_tag_posts_for(tags) → 标签页新发文后陈旧 ≤120s(web 端 update.rs:195 会失效)。
M7 · 分页标签缓存永不失效 — src/cache.rs:469-473
invalidate_posts_by_tag 只清 PostsByTag 键,不清 PostsByTagPage{tag,page,per_page} → 分页标签列表陈旧 ≤120s。注:分页路径前端未用到(pages/tags.rs 总传 None,None),属半死 API。
M8 · 限流器无 key GC + XFF 可伪造 — src/api/rate_limit.rs
(a) 7 个 keyed governor limiter(+ MCP 一个)从不调 retain_recent,公开 IP-keyed 桶在 IP 轮换攻击下内存无界增长;
(b) TRUSTED_PROXY_COUNT≥1 时客户端可伪造 XFF 控制自己的限流 key(逃逸 + 加剧 a)。
M9 · export.rs 原始 DB 错误泄露到 HTTP 响应 — src/api/database/export.rs:138,154
format!("查询失败:{e}") 把 tokio_postgres 错误(含 SQL/表名/内部细节)直接返回 body,违反全仓库 AppError 脱敏哲学(error.rs 测试明确锁定"不泄露 SQL")。
M10 · start_exec 与 stream 版 ~120 行重复 — src/api/code_runner/execute.rs:121-211 vs 244-340
信号量→stage→clamp→run→match 几乎完全相同,唯一差异是调用 stream 版还是非 stream 版 runner。两条路径必然漂移。建议抽 spawn_exec_task(lang_key, req, stream_tx: Option<Sender>)。
M11 · SQL 控制台全量物化结果集再客户端截断 — src/api/database/sql_console.rs:376-394
读路径 client.query(stmt_sql) 全量物化后在客户端截断 500 行。SELECT * FROM big_table 会把整张表拉进进程内存,仅靠 statement_timeout 兜底。建议追加 LIMIT 或 query_raw + take。
M12 · admin 文章列表吞掉拉取错误 — src/pages/admin/posts.rs:172
let _error = paginated.error; 把失败误报为"暂无文章"(兄弟 trash tab posts_trash.rs:62,135-142 正确处理了 error 分支)。
三、⚪ 死代码(Dead Code)
四、🔁 系统性代码重复(最大冗余来源)
冗余集中在两条轴:(A) api/ ↔ mcp/tools/ 镜像 ,(B) api/posts/ 内部自拷贝 。
R1 · MCP 工具镜像 api 业务逻辑(~700 行) — mcp/tools/posts.rs 逐行移植 api/posts/{create,update,delete,trash}.rs;comments.rs 镜像 api/comments/{list,update}(含相同的 WITH RECURSIVE ancestors CTE);media.rs 镜像 api/upload.rs(magic bytes、转码、CAS dedup SQL、日期目录命名);settings.rs 镜像 api/settings.rs + tasks/post_purge.rs(三份回收站设置读写)。已有 bug 因重复而扩散 (M2 标签 count 在 api 与 mcp 各一份;post_purge.rs:45,55 硬编码 false/30 而另两份用 DEFAULT_* 常量)。
R2 · MCP 跨文件 helper 四重拷贝 — require_scope/internal()/ok_json 在 posts.rs/comments.rs/media.rs/tags.rs 各一份完全相同;require_admin 在 settings.rs+runner.rs 两份。建议抽 mcp/tools/common.rs,删 ~10 份拷贝。
R3 · clean_tags/sync_tags/sync_asset_refs 因 pub(super) 被逐字拷进 MCP — api/posts/helpers.rs:139-252 vs mcp/tools/posts.rs:666-722,拷贝处注释自承「镜像 … 因 pub(super) 不可跨模块调用」。同文件 extract_asset_paths 已是 pub(crate) 并被共享——先例已存在。建议放宽为 pub(crate),删 MCP 拷贝(R1 的最大杠杆)。
R4 · render+metrics 块重复 6× — spawn_blocking render → toc_html None-if-empty → auto_summary → PostStatus → cover → count_words → reading_time:create.rs:66-84、update.rs:45-63、rebuild.rs:67-92 + 188-208、mcp/posts.rs:68-93 + 178-203。建议抽 helpers.rs::render_post_fields()。
R5 · 标签获取 SQL 重复 9× — SELECT t.name FROM tags JOIN post_tags … WHERE pt.post_id=$1:update.rs:110、delete.rs:47、trash.rs:57,133,214,307(+ANY 变体 390)、mcp/posts.rs:257,449,513。建议 fetch_post_tags() helper。
R6 · 缓存+SSR 失效「连祷」序列 6+× — invalidate_post_metadata+invalidate_post_by_slug+invalidate_tag_posts_for+invalidate_ssr_route+invalidate_ssr_all_public+bump_global_generation,在 create/update/delete/trash + mcp 多处。建议 cache::invalidate_for_post_write(slugs, tags) 单一来源。
重复常量 / 工具函数(应集中)
五、🟡 轻微(MINOR)— 文档漂移 / 风格(精选)
六、✅ 整体健康项(无需改动)
panic="abort" 约定执行良好 :12 处 panic! 全在 #[test] 或文档化的不可达 stub;非测试 expect() 均为带说明的真不变量;unwrap() 零违规。
AppError 脱敏分层 除 export.rs(M9)外全部正确遵守;error.rs 源链日志 + 静态客户端消息。
DB 连接池 防雪崩正确:pool-full Timeout 不重试,只对 Backend 错误退避;get_conn/get_conn_for_startup 分离合理。
迁移系统 (advisory lock + 单事务/迁移 + 回滚 + 数组/磁盘文件一致性编译测试)干净。
MCP crypto (AES-256-GCM,每次 OsRng nonce)安全;MCP router Origin/Host 白名单 + DNS-rebinding 防护 + dev-only 0.0.0.0 正确门控。
Docker 隔离 测试固化(cap_drop ALL、no-new-privileges、pids_limit、memory_swap==memory、tmpfs exec 仅 /tmp)。
theme.rs 的 Theme/ResolvedTheme 模型堪称典范;forms.rs FormSelect 无障碍完备且翻转逻辑有测试。
七、建议优先级
立即修复(真实 bug / 稳定性) :C1+C2(katex \pu + UTF-8,一行改动)→ C3(docker 挂起,DoS 级)→ C5(panic 崩溃)→ M1(越权)→ M2(公开数据错)→ M5(SSR 失效漏)。
重构(降重复防漂移) :D1(删/接 resources.rs + 收窄 allow)→ R2(抽 MCP common)→ R3(放宽 helpers 可见性,R1 的最大杠杆)→ R4/R5/R6(抽 render/tags/失效三件套)。
背景
对全仓库(~44k LOC,184 个 Rust 文件)做的深度代码审查,覆盖两条主线:①代码质量(功能 bug / 安全 / 性能 / 一致性)与 ②死代码/冗余(整模块死代码、系统性重复、死分支)。
方法:编译器权威 dead-code 扫描(
--force-warn dead_code)+ 4 路并行深度审查(API / MCP+Infra / 前端 / 跨模块去重),关键发现均已回源码核对。仅记录编译器无法发现或漏报的项;不包含 Dioxus#[server]函数的unreachable_pub噪音(HTTP 调用,编译器看不到调用图)。统计:严重 5 / 中等 12 / 死代码 12 / 系统性重复 6 / 轻微与文档漂移 ~20 / 重复常量 10+。下方 checkbox 可逐项勾选追踪。
一、🔴 严重(CRITICAL)— 功能 Bug / 安全 / 进程级风险
C1 · katex
\pu渲染从未生效(off-by-one) —src/api/katex.rs:148expand_chem中\pu是 3 字节(\,p,u),{在i+3(与\ce相同),但代码写Some(i+4)。结果\pu{9.8 m/s^2}永远匹配不上,mhchem::pu()从不触发,所有\pu物理单位公式渲染为红色废字。自 mhchem 初次提交(bcd1395)起存在。现有测试
mhchem_pu_units_renders是空测试:它只断言!contains("katex-error"),而 katex-rs 对未知命令走 color node(无 katex-error class),无论转译与否都通过。修复:
Some(i+3);测试改为断言转译产物(含mathrm等)。C2 · katex
expand_chem破坏多字节 UTF-8 —src/api/katex.rs:172out.push(bytes[i] as char)按单字节 Latin-1 转 char。任何含\ce/\pu且含非 ASCII(如中文\text{浓度})的公式会被破坏成乱码。修复:用
char_indices/ 对 gap 用push_str。C3 · 代码执行器 SSE 流可无限挂起 → 跑全部并发槽 DoS —
src/infra/docker.rs:551-558run_in_container_stream的log_reader先结束分支(输出超限时触发):注释写「短超时,避免无限等」,但docker.wait_container().next().await无超时、无 kill。while True: print()式程序会永久挂起 → (a)RUNNER_SEMAPHORE许可不释放(默认 4 并发 → 4 个即 DoS 全部代码执行器),(b)ContainerGuard不 drop → 容器永久运行烧 CPU。该端点对非 admin 用户可达(仅限流)。修复:在该分支用
timeout(limits.timeout_secs, wait_container),超时则kill_container。C4 · Dioxus 渲染纯度违规 —
src/components/code_runner/runner.rs:68-71组件 body 里
source_prop_signal.set(source.clone())—— 项目dioxus-render-purityskill 明令禁止的「prop 镜像进 signal + 渲染期 set」反模式,会触发额外一次渲染。Dioxus 0.7 的 prop 本身是响应式的,下方同步use_effect(L~207)可直接读sourceprop。修复:删掉
source_prop_signal,让同步 effect 直接捕获source。C5 · 图片缓存清理 panic 致进程崩溃 —
src/tasks/image_cache_cleanup.rs:57now - max_age在IMAGE_DISK_CACHE_MAX_AGE_HOURS设极大值时SystemTime下溢 panic。releasepanic="abort"→ 整个进程崩溃;max_age_hours * SECS_PER_HOUR也可能溢出。修复:
checked_sub+min(sane_cap),unwrap_or(SystemTime::UNIX_EPOCH)。二、🟠 中等(MODERATE)— 性能 / 一致性 / 数据正确性
M1 · MCP trash/delete 缺 author 归属校验 —
src/mcp/tools/posts.rs:~437,~499trash_post/delete_post用WHERE id = $1 …,无author_id过滤;而同文件update_post/publish_post都有WHERE author_id = $2。任意 write-scope token 可删除/废弃他人文章。鉴权同文件内不一致。M2 · 标签文章数 bug(api + mcp 双重镜像) —
src/api/posts/tags.rs:33+src/mcp/tools/read.rs:~290COUNT(pt.post_id)数了草稿/已删文章(posts JOIN 在 ON 子句里,count 没用p.id)。应改COUNT(p.id)。影响公开标签云(缓存 300s)和 MCPlist_tags。已是重复代码漂移的实例(见 R1)。M3 · backup.rs 在 async fn 里阻塞 tokio worker —
src/api/database/backup.rs:167-190,444-456std::process::Command+child.wait_with_output()跑 pg_dump/psql(可能数十秒~分钟),违反spawn_blocking约定。L368read_to_string把整个备份文件读进内存只为验首行签名;L497-512list_backups对每个 .sql 文件全量读入只为取第三行-- mode:。修复:包
spawn_blocking(或tokio::process/tokio::fs),读首行用BufReader。M4 · 评论 markdown 渲染未走 spawn_blocking —
src/api/comments/create.rs:148render_comment_markdown(pulldown + syntect + KaTeX,CPU 密集)直接跑在 async runtime。posts/* 都包了spawn_blocking。评论里的代码块是攻击者可控的 CPU 负载。M5 · 回收站批量操作漏 SSR 失效 —
src/api/posts/trash.rs:347-354,412-419batch_purge_posts/empty_trash只调bump_global_generation(),漏调invalidate_ssr_all_public()(单条 purge/restore 都调了)。批删/清空回收站后 SSR 缓存的列表/归档页仍显示已删文章。bump_global_generation自身注释也说"当前不会使 SSR 缓存失效"。M6 · MCP publish_post 漏标签缓存失效 —
src/mcp/tools/posts.rs:~405漏
cache::invalidate_tag_posts_for(tags)→ 标签页新发文后陈旧 ≤120s(web 端update.rs:195会失效)。M7 · 分页标签缓存永不失效 —
src/cache.rs:469-473invalidate_posts_by_tag只清PostsByTag键,不清PostsByTagPage{tag,page,per_page}→ 分页标签列表陈旧 ≤120s。注:分页路径前端未用到(pages/tags.rs总传None,None),属半死 API。M8 · 限流器无 key GC + XFF 可伪造 —
src/api/rate_limit.rs(a) 7 个 keyed governor limiter(+ MCP 一个)从不调
retain_recent,公开 IP-keyed 桶在 IP 轮换攻击下内存无界增长;(b)
TRUSTED_PROXY_COUNT≥1时客户端可伪造 XFF 控制自己的限流 key(逃逸 + 加剧 a)。M9 · export.rs 原始 DB 错误泄露到 HTTP 响应 —
src/api/database/export.rs:138,154format!("查询失败:{e}")把 tokio_postgres 错误(含 SQL/表名/内部细节)直接返回 body,违反全仓库 AppError 脱敏哲学(error.rs测试明确锁定"不泄露 SQL")。M10 · start_exec 与 stream 版 ~120 行重复 —
src/api/code_runner/execute.rs:121-211 vs 244-340信号量→stage→clamp→run→match 几乎完全相同,唯一差异是调用 stream 版还是非 stream 版 runner。两条路径必然漂移。建议抽
spawn_exec_task(lang_key, req, stream_tx: Option<Sender>)。M11 · SQL 控制台全量物化结果集再客户端截断 —
src/api/database/sql_console.rs:376-394读路径
client.query(stmt_sql)全量物化后在客户端截断 500 行。SELECT * FROM big_table会把整张表拉进进程内存,仅靠 statement_timeout 兜底。建议追加LIMIT或query_raw + take。M12 · admin 文章列表吞掉拉取错误 —
src/pages/admin/posts.rs:172let _error = paginated.error;把失败误报为"暂无文章"(兄弟 trash tabposts_trash.rs:62,135-142正确处理了 error 分支)。三、⚪ 死代码(Dead Code)
D1 · 整模块死代码:
src/mcp/resources.rs(273 行全部死)完整的资源子系统(
list_published_resources/read_post_resource/post_resource_template/ResourcePage/URI 助手/游标编解码/分页常量),但ServerHandler从未 overridelist_resources/read_resource,故无一处可达。模块自身 doc 声称"server.rs 把它们接到 ServerHandler",接线从未完成。被src/main.rs:47的#[allow(dead_code)] mod mcp;模块级静默掩盖(会掩盖未来的 mcp 死代码)。内部还藏 bug:
post_resource_template()传字面量"{POST_URI_PREFIX}{slug}"(缺format!),测试 L290 把 bug 写进断言。建议:删除整个文件 + 收窄 main.rs 的 allow;或接进 ServerHandler 并修 bug。
D2 ·
src/models/user.rs:32-118— 完整User结构(含 password_hash)+From<User> for SessionUser+From<User> for PublicUser生产环境从未使用(auth.rs:402直接从 row 构造 SessionUser);仅自身测试构造。~65 行生产死代码。D3 ·
src/api/posts/helpers.rs:57—row_to_post_full(_client: &Client, row)的_client参数从未使用(两个调用点都传了活 client)。D4 ·
src/api/mhchem.rs:959-969—go_inner与field_str字节级完全相同的函数(各被调 17/19 次)。删一留一。D5 ·
src/api/mhchem_tables.rs:56-93—tex状态机表不可达(to_tex只被 "ce"/"pu" 调用,无go()用 "tex")。~40 行死数据(机械移植残留)。D6 ·
src/api/markdown.rs:10-13+src/api/comments/markdown.rs:11-14—clean_html/clean_comment_html各是一行直通包装器,仅同文件调用,无外部 caller(comments/markdown 模块甚至未被 mod.rs re-export)。D7 ·
src/api/sanitizer.rs:150-170—is_safe_data_uri+allow_data_uri配置字段生产死(两调用点硬编码false)。D8 ·
src/api/code_runner/mod.rs:47—ExecStatus::RateLimited服务端从不构造(限流在 insert 前拒绝;仅前端有 label)。serde 兼容保留,建议加注释。D9 ·
src/api/database/mod.rs:6-7— 过时规划注释,列出"后续 task 新增"的模块其实都已存在。D10 ·
src/components/comments/item.rs:24,38—let refresh_trigger = ctx.refresh_trigger; … let _ = refresh_trigger;死绑定。D11 ·
src/components/post/post_nav_links.rs:28,42— 两个空 no-oponclick: move |_| {}。D12 ·
src/components/header.rs:40—use_memo(|| "mobile-nav-menu".to_string()),对常量做 memo,用&str即可。四、🔁 系统性代码重复(最大冗余来源)
冗余集中在两条轴:(A)
api/↔mcp/tools/镜像,(B)api/posts/内部自拷贝。R1 · MCP 工具镜像 api 业务逻辑(~700 行) —
mcp/tools/posts.rs逐行移植api/posts/{create,update,delete,trash}.rs;comments.rs镜像api/comments/{list,update}(含相同的 WITH RECURSIVE ancestors CTE);media.rs镜像api/upload.rs(magic bytes、转码、CAS dedup SQL、日期目录命名);settings.rs镜像api/settings.rs+tasks/post_purge.rs(三份回收站设置读写)。已有 bug 因重复而扩散(M2 标签 count 在 api 与 mcp 各一份;post_purge.rs:45,55硬编码false/30而另两份用DEFAULT_*常量)。R2 · MCP 跨文件 helper 四重拷贝 —
require_scope/internal()/ok_json在posts.rs/comments.rs/media.rs/tags.rs各一份完全相同;require_admin在settings.rs+runner.rs两份。建议抽mcp/tools/common.rs,删 ~10 份拷贝。R3 ·
clean_tags/sync_tags/sync_asset_refs因pub(super)被逐字拷进 MCP —api/posts/helpers.rs:139-252vsmcp/tools/posts.rs:666-722,拷贝处注释自承「镜像 … 因 pub(super) 不可跨模块调用」。同文件extract_asset_paths已是pub(crate)并被共享——先例已存在。建议放宽为pub(crate),删 MCP 拷贝(R1 的最大杠杆)。R4 · render+metrics 块重复 6× —
spawn_blocking render → toc_html None-if-empty → auto_summary → PostStatus → cover → count_words → reading_time:create.rs:66-84、update.rs:45-63、rebuild.rs:67-92+188-208、mcp/posts.rs:68-93+178-203。建议抽helpers.rs::render_post_fields()。R5 · 标签获取 SQL 重复 9× —
SELECT t.name FROM tags JOIN post_tags … WHERE pt.post_id=$1:update.rs:110、delete.rs:47、trash.rs:57,133,214,307(+ANY 变体 390)、mcp/posts.rs:257,449,513。建议fetch_post_tags()helper。R6 · 缓存+SSR 失效「连祷」序列 6+× —
invalidate_post_metadata+invalidate_post_by_slug+invalidate_tag_posts_for+invalidate_ssr_route+invalidate_ssr_all_public+bump_global_generation,在 create/update/delete/trash + mcp 多处。建议cache::invalidate_for_post_write(slugs, tags)单一来源。重复常量 / 工具函数(应集中)
EMAIL_REGEX两份完全相同:auth.rs:38+comments/helpers.rs:95。hash_token两份逐字相同:auth/session.rs:22+mcp/auth.rs:45。MAX_FILE_SIZE两份:upload.rs:31+mcp/tools/media.rs:24(均5*1024*1024)。MIGRATE_STARTUP_TIMEOUT_SECS解析 3 份:main.rs:145、db/pool.rs:157、244(默认值 30 相同)。list.rs:139字符循环 vssearch.rs:59+mcp/read.rs:200replace 链。format_bytes两份且分歧:assets.rs1 位小数 vssystem/mod.rs:1292 位小数。urlencode两份:system/export.rs:131+backup.rs:296(手写编码器,应进 utils)。var(k).ok().and_then(parse).unwrap_or(d)散落 ~20 处;rate_limit.rs:26已有env_or但私有未复用。formatted_date两份相同 body:models/post.rs:83(Post)+128(PostListItem)。五、🟡 轻微(MINOR)— 文档漂移 / 风格(精选)
src/api/image.rs:746get_image_dimensions读整个文件只解析头部维度(有 24h 缓存,低优先)。src/api/sanitizer.rs:369unwrap_or_default():rewrite 出错静默返回空串(整篇文章 body 被抹掉,无日志)。src/components/post/post_cover.rs:38-47+pages/admin/write.rs:685-692潜在 URL bug:含?的资源 URL 被 split 后用&w=…拼回(缺?)。当前数据不触发。src/pages/admin/system/db_status.rs:47-55生产路径残留console.logDEBUG 计时日志。src/pages/admin/assets.rs:162-171每次 render clone 最多 60 个 AssetDto;HashSet 每次切换 clone 整个 set(可用write().insert)。src/components/post_card.rs:65每次 renderpost.tags.clone()。src/api/rate_limit.rs:70注释「0.2 req/s」实际默认 1 req/s(doc 漂移)。src/api/posts/search.rs:2-4模块 doc 称 pg_trgm,实现是 ILIKE(doc 漂移)。src/api/database/sql_console.rs:191-194is_read_onlydoc 称覆盖 SELECT/EXPLAIN/SHOW/WITH,实际 match 只覆盖 Query+Explain →SHOW落进写分支会误触发全缓存失效。src/api/comments/check.rs:24check_pending_status(ids)未认证端点,ids数组无长度上限。src/pages/admin/comments.rs:~148切换 filter 不重置路由 page,可能落到空页;?status=仅挂载时读一次。src/pages/admin/posts_trash.rs+comments.rs乐观变更忽略 server Result,失败时 UI 与 DB 不一致。src/pages/admin/asset_picker.rs:36-54搜索每次按键触发list_assets,无 debounce、无 stale 响应保护(assets.rs有 debounce 可借鉴)。src/components/skeletons/post_card_skeleton.rs:15skeleton 样式陈旧(bg-white/rounded-lg/gray border)与现 PostCard(paper-entry/rounded-[2rem])不匹配 → 加载时视觉跳变。src/components/admin_layout.rs:148-155WriteEdit路由回退到AdminDashboardSkeleton而非WriteSkeleton,编辑页会闪错骨架屏。六、✅ 整体健康项(无需改动)
panic="abort"约定执行良好:12 处panic!全在#[test]或文档化的不可达 stub;非测试expect()均为带说明的真不变量;unwrap()零违规。error.rs源链日志 + 静态客户端消息。Timeout不重试,只对 Backend 错误退避;get_conn/get_conn_for_startup分离合理。七、建议优先级
立即修复(真实 bug / 稳定性):C1+C2(katex
\pu+ UTF-8,一行改动)→ C3(docker 挂起,DoS 级)→ C5(panic 崩溃)→ M1(越权)→ M2(公开数据错)→ M5(SSR 失效漏)。重构(降重复防漂移):D1(删/接 resources.rs + 收窄 allow)→ R2(抽 MCP common)→ R3(放宽 helpers 可见性,R1 的最大杠杆)→ R4/R5/R6(抽 render/tags/失效三件套)。