596 Commits

Author SHA1 Message Date
xfy
37d7ec49eb fix(database): harden SQL console guards + review fixes
最终 code review 发现的安全/正确性修复:

- C1 (critical): execute_one 改用 stmt.to_string() 重序列化的单条语句 SQL
  而非原始整段 SQL,保证执行的语句与护栏检查的 AST 一致;杜绝 allow_multi
  时整段 SQL 被重复执行、读写分类与实际执行解耦。
- C2 (critical): check_guards 在 AST 层结构性禁止 DROP SCHEMA(ObjectType
  匹配),不再仅靠字符串预检——防 SQL 注释/空白绕过。
- I2: 备份/恢复在 DATABASE_URL 为空时提前失败任务,而非传空串给 pg_dump/psql。
- I4: 回退备份进度计算用 u32 避免 >255 表时截断/溢出。
- I5: SQL 导出用真实表名(table 模式)而非硬编码 "export"。
- M7: 移除 SqlResult 中未使用的 total_estimate 字段。
2026-06-29 19:24:06 +08:00
xfy
6e94db0c2b feat(admin): add backup/restore tab (dual-mode + task progress polling)
新增 dashmap 依赖。create_backup 探测 pg_dump 优先(含 schema,签名头前置)、
不可用回退纯 SQL(COPY TO STDOUT 逐表,按表精确进度)。restore_backup 校验
签名头(仅本系统备份)+ 路径穿越防护 + 二次确认,探测 psql 执行。
list_backups/delete_backup 管理备份,GET /api/database/backups/{name} 下载
(admin 鉴权 + 白名单)。tasks.rs DashMap 进度表(1h 惰性 GC),前端每 1.5s
轮询 get_task_progress 显示进度条/stage,完成后刷新列表。
backups/ gitignored 不直接暴露。AGENTS.md 同步新增 CodeMirror 子项目与
数据库管理章节。
2026-06-29 19:16:11 +08:00
xfy
5c4b2a53d1 feat(admin): add data export tab (Axum streaming, SQL/CSV)
新增 GET /api/database/export Axum 流式路由(镜像 upload.rs 的 cookie→admin
鉴权),支持按表/按查询导出。CSV 用 COPY ... TO STDOUT WITH CSV 流式
(大表不 OOM),SQL 逐行拼 INSERT(含类型化转义)。表名白名单 + 查询
AST 只读强制。前端 ExportTab 触发 window.open 下载。
2026-06-29 19:01:16 +08:00
xfy
a49f47c8a6 feat(admin): add SQL console tab (read-write + 4 guards + sqlparser)
新增 sqlparser 依赖(optional+server),AppError 加 BadRequest 变体(护栏
返回动态用户可见消息)。execute_sql server function 全读写执行,4 道护栏:
(1) sqlparser AST 高危语句闸门——DROP DATABASE/SCHEMA 字符串预检绝禁、
DROP/TRUNCATE/ALTER 需勾选「我了解后果」;(2) 无 WHERE 的 UPDATE/DELETE 拒绝;
(3) 复用 STATEMENT_TIMEOUT_SECS 超时上限;(4) 前端写操作二次确认。
默认禁多语句,结果 500 行截断。get_db_schema 拉表/列供 CodeMirror 补全。

前端 SqlConsoleTab:CodeMirror 编辑器(Vim + Catppuccin 主题跟随站点 +
实时 schema 补全)+ 选项 toggles + 结果表格 + EXPLAIN 输出。
2026-06-29 18:56:15 +08:00
xfy
c4c490b881 feat(admin): add server status tab (sysinfo host metrics + cache hit rate)
新增 sysinfo 依赖(optional+server),sysinfo_sampler 后台采样任务
(SYSINFO_SAMPLE_SECS 可配,默认 0.5s)周期刷新 CPU/内存/磁盘/load 到 RwLock
快照,get_server_status 只读快照零成本。cache.rs 给 9 个缓存加 AtomicU64
hit/miss 计数 + cache_stats() 聚合命中率。

前端 ServerStatusTab:应用内指标(运行时间/连接池/会话/CPU/内存/磁盘/load)+
缓存命中率表 + 刷新按钮 + 自动刷新开关(500ms/1s/2s/5s/手动,默认手动)。
2026-06-29 18:45:02 +08:00
xfy
7553dcf405 feat(admin): add database status tab (tables/connections/migration version)
get_db_status server function 查 pg_catalog 聚合:数据库总大小/连接数/
表清单(行数估算+大小+死元组)/索引占用 Top10/活跃连接(过滤自身)/迁移版本。
前端 DbStatusTab:概览卡片 + 表/索引/连接表格 + 刷新按钮 + 自动刷新开关
(1s/2s/5s/30s/手动,默认手动;wasm 端用 web_sys setTimeout 轮询,无新依赖)。
2026-06-29 18:36:08 +08:00
xfy
81a4196e43 feat(admin): add /admin/system route shell with 5 tabs
新增系统管理入口页(数据库状态/服务器状态/SQL 控制台/导出/备份恢复),
顶部 tab 切换(用 use_signal 不深链)。路由注册 + 导航加「系统」项。
tab 内容待后续 task 填充。
2026-06-29 18:26:24 +08:00
xfy
c74e59485d feat(editor): add codemirror-editor subproject + Rust bridge
新建 libs/codemirror-editor(pnpm/Vite IIFE/@catppuccin/codemirror Catppuccin
Latte+Mocha 主题/@codemirror/lang-sql schema 补全/@replit/codemirror-vim),
输出 public/codemirror/。Rust 桥接 codemirror_bridge.rs 镜像 tiptap_bridge.rs
(Reflect::get 取对象字面量 + EditorHandle 持有闭包 + Drop→destroy)。
接入 Makefile/Dioxus.toml/.gitignore。

注意:thememirror 不含 catppuccin 主题,改用官方 @catppuccin/codemirror@1.0.3
(导出 catppuccinLatte/catppuccinMocha)。
2026-06-29 18:24:38 +08:00
xfy
5e4f32f2ab docs(main): fix misleading APP_BASE_URL comment
warn_if_app_base_url_unset() is unconditional—localhost dev also
emits a WARN (per csrf.rs doc). The main.rs side note claimed
localhost stays silent, contradicting the implementation.
2026-06-29 15:59:54 +08:00
xfy
431e6e1db4 fix(lint): resolve clippy warnings in comment_storage tests and trash.rs
- comment_storage.rs: group digits consistently (3600_000 → 3_600_000)
- trash.rs: remove unused `use super::*` in test module
2026-06-29 15:54:49 +08:00
xfy
48a328f553 feat(admin): add decorative image to tiptap editor with focus-within fade effect 2026-06-29 15:45:24 +08:00
xfy
962bef2276 fix(ssr-cache): log generation bump and effective SSR_CACHE_SECS
Dioxus 0.7 的增量渲染器把 IncrementalRenderer 封装在 FullstackState 内部
(pub(crate)),应用侧无法拿到句柄调用 invalidate(),世代号机制只是"未来就绪"
状态。实际失效仍依赖 SSR_CACHE_SECS 兜底 TTL,但此前写入路径完全静默,部署者
无法从日志判断"文章已写入、只是 SSR 缓存未失效",易误判成 DB/发布问题。

- bump_global_generation() 增加 info 日志(带 new_generation),诚实说明 Dioxus
  0.7 不读取此值、需等 TTL 过期。
- main.rs 启动时打印生效的 SSR_CACHE_SECS(滞后上界),提示可调小缩短滞后。
2026-06-29 15:42:40 +08:00
xfy
45811df96e fix(comments): add md-content class to comment item to fix syntax highlighting and blank line issues 2026-06-29 15:42:08 +08:00
xfy
656e35c79f fix(comments): move decorative image properly inside textarea background layer 2026-06-29 15:20:48 +08:00
xfy
0b53e968c1 feat(comments): add decorative image to comment input with focus fade effect 2026-06-29 14:42:03 +08:00
xfy
fbd706b198 style: format files and update about page content
- Update about page content

- Format code in admin pages, tags, and theme modules
2026-06-29 14:31:04 +08:00
xfy
d1b5b8ee4e fix(ui): suppress unused variable warning for active param in server build 2026-06-29 14:29:45 +08:00
xfy
ae770cae3a feat(ui): use EmptyState component for search no results 2026-06-29 14:14:03 +08:00
xfy
23f0cc6222 fix(ui): adjust search placeholder size and position 2026-06-29 14:10:21 +08:00
xfy
b366c00e74 fix(ui): enlarge and better center search placeholder image 2026-06-29 14:07:35 +08:00
xfy
b666472193 fix(ui): add margin between post count and list in tag detail page 2026-06-29 14:05:47 +08:00
xfy
3f7d112898 feat(pages): add empty placeholder image to search page 2026-06-29 13:55:08 +08:00
xfy
528a79fb83 feat(pages): add empty state to tags and tag detail pages 2026-06-29 13:50:05 +08:00
xfy
373498870a style: apply cargo fmt to workspace
Some checks failed
CI / check (push) Failing after 5m35s
CI / build (push) Has been skipped
2026-06-29 13:47:40 +08:00
xfy
5e1becf116 fix(admin): explicitly set pointer-events auto for trash settings panel to resolve click bug 2026-06-29 13:46:05 +08:00
xfy
ef8af77a08 style(admin): format trash.rs 2026-06-29 13:42:29 +08:00
xfy
fdaefae027 test: fix test_auto_purge_settings_has_transition_class false positive 2026-06-29 13:37:59 +08:00
xfy
768dbc9510 feat(admin): add smooth slide-down animation to trash auto-purge settings panel 2026-06-29 13:34:43 +08:00
xfy
a47a5a6ac9 feat(admin): update empty state for trash page 2026-06-29 13:26:45 +08:00
xfy
c69a632e75 fix(ui): trigger FilterTabs indicator animation on click 2026-06-29 13:24:52 +08:00
xfy
3daa09efc2 fix(ui): fix WASM compilation error in FilterTabs indicator animation 2026-06-29 13:22:22 +08:00
xfy
ec5f933191 feat(ui): add smooth sliding animation to FilterTabs active indicator 2026-06-29 13:20:39 +08:00
xfy
d91a56f8b2 refactor(admin): extract comments filter tabs into FilterTabs component 2026-06-29 13:15:50 +08:00
xfy
79af7d345e fix(admin): 修复重建内容后标题行纵向错位
点击「重建内容 / 重建全部」后,结果消息撑高了 RebuildCacheBar,外层
header 的 items-center 据此重新居中,把「文章管理」标题与「+ 写文章」按钮
整体下移,而重建按钮停在容器顶部,造成三者不再同处一行。

根因:c024de1 抽取 RebuildCacheBar 时把结果消息纳入子组件的 flex-col 文档流
(重构前消息是页面级兄弟节点,不参与 header 居中计算)。

修复:根容器改 relative,结果消息用 absolute 悬挂于按钮行下方、脱离文档流,
组件盒高恒等于按钮行高度,无论消息是否出现都不影响 header 的垂直居中。

验证:dx check + cargo clippy 全绿。
2026-06-29 11:49:28 +08:00
xfy
a1aef5bda9 fix(admin): add cursor-pointer to comment status filter buttons 2026-06-29 11:41:52 +08:00
xfy
dbe95f1173 feat(admin): use global EmptyState component in comments management page 2026-06-29 11:39:32 +08:00
xfy
72d21c6670 feat(admin): use global EmptyState component in posts management page 2026-06-29 11:38:50 +08:00
xfy
46e9a29f1e fix(theme): 消除 ThemeToggle 在 wasm/server 构建的 unused_mut 警告
Some checks failed
CI / check (push) Failing after 6m25s
CI / build (push) Has been skipped
dx build 的 wasm target 报两条 unused_mut:

- theme (160行): wasm 端的 theme.set 被非 wasm cfg 分支剥离,写操作转移
  到副本 theme_clone,theme 本身在 wasm 下只被读取/Copy。加
  #[cfg_attr(target_arch = "wasm32", allow(unused_mut))]——仅 wasm 端压住,
  server 端仍需 mut(theme.set(next) 实际存在)。
- gen_clone (191行): 闭包内只读 gen_clone(),从未 set,两端都不需 mut,
  直接删除。

顺带把原来无条件挂在 click_gen 上的 #[allow(unused_mut)] 改成
#[cfg_attr(not(target_arch = "wasm32"), ...)]——click_gen 只在 wasm 分支被
set(189行),server 端该分支被剥离。改为按 cfg 方向标注,与 theme 对称,
语义更精确(不再无差别压住 wasm 端的检查)。

匹配项目既定模式(post_cover.rs:20 的 cfg_attr allow)。零行为变更,
dx check / cargo check(双 target) / clippy / theme 测试均通过。
2026-06-29 11:31:57 +08:00
xfy
b923851284 docs(main): 注明 CompressionLayer 默认跳过 image/*
Some checks failed
CI / check (push) Failing after 5m37s
CI / build (push) Has been skipped
避免后续误以为压缩层会对 WebP/PNG/JPEG 等已是压缩格式的图片再次
压缩而浪费 CPU。tower-http 的 DefaultPredicate 开箱即跳过 image/*
(例外 image/svg+xml)、gRPC、SSE 及 <32 字节响应,无需额外配置。

纯文档性注释,无行为变更。
2026-06-29 11:16:30 +08:00
xfy
74f8c212f6 feat(csrf): warn at startup when APP_BASE_URL is unset
APP_BASE_URL 未设置时 trusted_origin 回退到请求 Host 头推导本站 origin,
反向代理后若 Host 头可被客户端影响存在 CSRF 绕过风险。.env.example 虽然
警告了,但默认值就是空——'cp .env.example .env 后忘改' 是最常见的部署失误。

加启动时一次性 WARN(与 image.rs 的启动告警同范式):
- warn_if_app_base_url_unset() 在 main.rs 启动序列调用,与 validate_database_url
  等配置告警归在一处
- 抽出纯函数 app_base_url_is_set() 承载判断逻辑,便于测试,打日志副作用与之解耦
- 5 个单测覆盖 unset/empty/whitespace/set/trim 边界

每请求路径的 trusted_origin 保持纯函数不变,不引入 dedup 状态或刷屏风险。
2026-06-29 11:02:58 +08:00
xfy
cb73a9b979 chore: fix cargo clippy warnings (unused import and false positive unused_mut) 2026-06-29 10:14:27 +08:00
xfy
178870427c feat(ui): refactor EmptyState to accept dynamic string props and add empty state to archives page 2026-06-29 10:11:00 +08:00
xfy
82a8c511fc fix(theme): remove dark class from AppRouter div 2026-06-26 18:24:00 +08:00
xfy
ba9db2d75a fix(theme): fix dark→light transition animation invisible
The static CSS rule 'animation: none' on ::view-transition-new(root)
caused the new (light) screenshot to be fully visible before the JS-
injected clip-path @keyframes kicked in. This meant the light layer
was already covering the dark layer, making the expanding circle
invisible.

Fix: move all VT pseudo-element styles (animation, mix-blend-mode)
into the dynamically injected <style> block in vt.ready, so they
apply atomically with the clip-path keyframes.

Also:
- Add transition:none !important during VT to prevent body's
  background-color 0.3s transition from producing a stale capture.
- Restore Dioxus theme.set() that was disabled for debugging, so
  the toggle icon and localStorage persist correctly.
- Use !important on injected animation to reliably override any
  UA default VT animations.
2026-06-26 17:50:45 +08:00
xfy
1e78c7f0f9 fix(doc): 修复 /doc 路由与 Dioxus 静态托管冲突的 panic
Dioxus 在 dev 用 nest_service("/doc", ServeDir) 托管 public/doc/ 目录,
手动在 static_routes 注册 /doc 会在 merge 时触发路由冲突 panic。

根因(dioxus-server serve_dir_cached):dev 模式对 public/ 下的目录用
nest_service 挂整个 ServeDir,注册了 /doc;我在 static_routes 加的
精确 /doc 路由与之同名冲突。

修复:
- main.rs: 移除手动注册的 /doc 与 /doc/ 重定向路由,交给 Dioxus 托管。
  文档深层路径 /doc/yggdrasil/* 由 ServeDir 直接服务。
- Makefile: doc 目标拷贝后额外生成 public/doc/index.html 重定向页,
  ServeDir 访问目录根时返回它,经 meta refresh + JS 跳转到
  yggdrasil/index.html,让裸路径 /doc 也能直达文档。
- build/clean 同步集成 make doc 与清理 public/doc。
2026-06-26 14:27:21 +08:00
xfy
30a861f910 docs(agents): 新增 yggdrasil-core 子工程章节
与 lightbox/tiptap-editor 并列,记录用途、构建、注入方式与
主题展开动画的 View Transitions 实现。同步更新 make test 注释
与 Build Artifacts 列表。
2026-06-26 14:09:46 +08:00
xfy
6979bd1010 feat(theme): 圆形展开主题切换动画(View Transitions API)
点击主题按钮时,新主题从按钮位置以圆形向外展开覆盖全屏。
JS 同步 toggle dark class 拍快照 + CSS clip-path circle keyframes;
Rust theme.set 事后对齐状态(use_effect 幂等)。
reduced-motion 与不支持 VT 的浏览器自动降级为瞬切。
2026-06-26 14:06:23 +08:00
xfy
ac1f92d816 refactor(core): 迁移 post-content 到 yggdrasil-core,删除 public/js
逐行 TypeScript 化(逻辑不变),改走 window.__initPostContent 全局入口。
post-content.js 已迁移进 libs/yggdrasil-core/,public/js/ 目录随之删除。
2026-06-26 13:57:48 +08:00
xfy
25e01a109b fix(write): 移除 cover_uploading 多余的 mut 绑定
本组件只读取(on_submit 拦截)与传递给 CoverUploader,写入全在子组件
内部。Signal 是 Copy 类型,.set() 不要求 mut 绑定,编译器的 unused_mut
警告是真阳性。
2026-06-26 11:07:31 +08:00
xfy
33db6dc5aa feat(doc): 文档托管到 /doc 路径
- main.rs: static_routes 加 /doc 与 /doc/ 的 301 重定向到
  /doc/yggdrasil/index.html,绕开 Router 的 /:..segments 兜底。
  文档深层路径由 Dioxus 自动托管 public/ 提供,无需额外代码。
- .gitignore: 忽略 public/doc 构建产物(配合 make doc 拷贝)
2026-06-26 11:04:29 +08:00