1530d3a2f4
style(admin/posts): 顶部重建按钮 loading 用 spinner 覆盖文字
...
工具条「重建内容」「重建全部」按钮 loading 时由「重建中...」文案
改为 spinner 居中覆盖、文字 opacity-40 衬底,与行内重建按钮的
loading 样式统一。
2026-07-03 10:46:57 +08:00
db87d3c886
fix(admin/posts): 重建支持并发 loading,spinner 覆盖文字
...
- rebuilding 信号由 Option<i32> 改为 HashSet<i32>,连续点击多篇
文章的重建按钮可同时处于 loading 态(单值会被后点的覆盖)
- loading 样式调整:按钮文案恒为「重建」,spinner 绝对定位覆盖在
文字上方居中,文字 opacity 调至 40% 衬底,不再切换文案
2026-07-03 10:40:11 +08:00
a5c00caa80
style(admin/posts): 操作列加宽横排,重建按钮 loading 用 spinner 图标
...
- 操作列宽 w-24 → w-44,三个按钮一行排开不换行
- 重建按钮 loading 态由「重建中...」文案改为 spinner 图标 + 「重建中」
- spinner 复用 public/icons/90-ring-with-gradient.svg,作为 SPINNER_SVG
常量收入 ui.rs(修正原 SVG 损坏的孤立 ':' 选择器,重命名 id/类名
避免多处内联时冲突),用 currentColor 继承按钮文字色
2026-07-03 10:26:14 +08:00
e55f2e12bd
refactor(admin/posts): 重建按钮文案改「重建」并去掉完成弹窗
...
按钮态文案与原有一致(「重建中...」);on_rebuild 改为静默执行,
仅按行切换 rebuilding 按钮态,完成后不再弹浏览器 alert。
2026-07-03 10:16:13 +08:00
0fe0387ea3
feat(admin/posts): 文章列表操作列新增「重建内容」按钮
...
新增 rebuild_post_content_html(post_id) server function,从 DB 读取
content_md(事务内 FOR UPDATE 锁行)重新渲染 content_html/toc_html
并更新 word_count/reading_time,按影响范围精准失效列表、slug 单篇
与搜索缓存。
PostRow 操作列在「编辑」「删除」之间插入「重建内容」按钮(鼠尾草绿
文字样式),按行禁用并切换「重建中...」文案,成功/失败弹出浏览器
提示,与删除按钮的反馈模式一致。
为避免与单篇 rebuilding 信号冲突,将原批量重建工具条信号 renaming
为 batch_rebuilding。
2026-07-03 10:13:15 +08:00
65b15509a7
chore(wasm): 清理 wasm32 target 下残留的 clippy lint
...
这些 lint 在 server build(默认 --all-features)下被 cfg 剥离故未暴露,
仅 wasm32 web-only target 触发。逐项清理:
- footer.rs: 两处 redundant closure(web_sys::window 直接传函数引用、
sync_visible 直接传闭包)+ let _ = unit 绑定(scroll_to_*)。
- hooks/event_listener.rs: 复杂类型 Rc<RefCell<Option<(Closure,T)>>>
抽 type ListenerState<T> 别名。
- system.rs: 两处单模式 match { Ok=>.., _=>{} } 改 if let。
- write.rs: split(|c| matches!(c,...)) 改 split([char; N]) 数组模式。
- codemirror_bridge.rs: let _ = instance.destroy() 删 unit 绑定,
与 tiptap_bridge.rs 同处写法对齐。
wasm32 与 server 两 target clippy -D warnings 均通过。
2026-07-02 17:15:27 +08:00
59359b18ce
refactor(hooks): 新增 use_paginated 并迁移 posts/trash 分页加载
...
按 Dioxus 0.7 custom hooks 范式,把后台分页列表页反复出现的
use_effect + spawn + (loading/items/total/error) 三件套收口到
use_paginated 组合式 hook。统一非 wasm cfg 处理(SSR 不发请求、
loading 立即置 false),调用方只关心 fetch 闭包与页码依赖。
迁移范围(按降级预案,只做最规整的两处):
- pages/admin/posts.rs:顺手补 error signal(原先吞掉 Err),向
trash 看齐,消除一致性债。
- pages/admin/trash.rs:直接替换,error 已有。
保留手写的形态(强行套 hook 反而更复杂):
- comments.rs:依赖 (page, status) 双 signal,status 是额外维度,
套不进 (page, per_page) 模型;改 hook 会为单一调用点变复杂。
- system.rs 三个 tab:load_once + use_future 轮询形态,与 hook 的
单次加载模型不契合。
- dashboard.rs:多源聚合(3 个并发 spawn 分别 set 不同 signal)。
list_posts / list_deleted_posts 是 server function,两端都生成
(wasm 端 client stub、server 端真实实现),故去掉原 cfg import。
2026-07-02 16:30:47 +08:00
62bd281684
fix(admin/posts): 重建结果消息改为独立行,修复悬浮溢出表格的错位
...
RebuildCacheBar 的「已重建 X 篇文章」结果消息原用 absolute top-full mt-2
悬挂在按钮行下方,但 mt-2(8px) + text-sm(20px) = 28px 大于外层 space-y-6
的 24px 间距,消息底边向下溢出 4px 侵入表格容器上沿;横向 left-0 又使消息
落在右上角按钮组正下方、覆盖状态/日期/操作列,语义位置错位。
将 rebuilding / rebuild_result 状态上提到 PostsPage:
- 结果消息在 header 与表格之间独立成行,进入文档流,吃 space-y-6 正常间距,
既不撑高 header 触发 items-center 重排(曾用 absolute 规避的旧问题),
也不脱离流溢进表格(absolute 引入的新问题)。
- RebuildCacheBar 改为接收父级 Signal props,只渲染按钮行本身。
2026-07-02 14:19:06 +08:00
eacc79ed83
feat(theme): 新增跟随系统主题模式与三态循环切换
...
Theme 枚举从 {Light, Dark} 扩展为 {Light, Dark, System},新增
ResolvedTheme 表示实际生效明暗。
- System 模式移除 localStorage 持久化,首屏防闪烁脚本因此自动回退到
prefers-color-scheme 分支,首屏零闪烁。
- use_theme_provider 提供 Theme + ResolvedTheme 双 context;resolved 是
theme 与 system_dark 的派生 memo。WASM 端注册 matchMedia change 监听,
系统偏好变化时实时同步 .dark class 与下游(CodeMirror 等)。
- ThemeToggle 由二态 toggle 改为三态循环(Light→Dark→System→Light),
图标按当前模式切换(太阳/月亮/显示器);仅当实际明暗翻转时才触发
圆形展开动画,避免 System 与同向 Light/Dark 间无意义动画。
- system.rs CodeMirror 传参改读 resolved,使 System 模式下系统偏好变化
时编辑器主题自动跟随。
2026-07-02 11:35:28 +08:00
9a218db478
refactor(assets): 编辑器背景图缩小至 w-24 并贴近右下角
...
原图无宽度约束,按原始尺寸渲染占满编辑器;现固定 w-24(与评论区
一致)并用 bottom-2 right-2 定位到右下角。一并提交新增的线条小狗
素材图与图标资源。
2026-07-02 11:22:34 +08:00
48a21564c4
refactor(assets): 背景图固定 opacity-10,移除动态透明度逻辑
...
动态判断不生效,统一固定为 10% 透明度,移除聚焦/有内容的条件类。
2026-07-02 11:17:41 +08:00
fe22d6dad8
feat(editor): tiptap 编辑器有内容时自动调淡背景图透明度
...
无内容时背景图保持 60% 透明度;当编辑器内有内容(非纯空白)时,
背景图淡出到 10%,与聚焦时行为一致。content signal 由 onUpdate
回调实时维护,输入即触发重渲染,无需额外状态。
2026-07-02 11:11:04 +08:00
48107d8e18
refactor(assets): 评论区与后台编辑器背景图换用 xiantiaoxiaogou_input_bg
2026-07-02 10:58:52 +08:00
9cf0e605d4
fix(admin): 修复服务器/数据库状态自动刷新失效 Bug
...
use_future 旧实现在同步闭包体内捕获 status/loading/error signal,
导致这些 signal 每次 .set() 后都触发 use_future 重建新 loop,
旧 loop 同时继续运行,造成并发 loop 指数增长(请求爆炸),
实际效果等同于自动刷新完全失效或行为异常。
修复:采用 Dioxus 0.7 官方推荐模式——
- use_future 闭包直接返回 async move {},不在同步层读任何 signal
- loop 内每次迭代读 refresh_interval/refresh_ms 的实时值
- 手动模式(interval = 0)改为 200ms yield + continue,
使切换到自动模式时最多延迟 200ms 生效,避免忙等
- sleep 结束后二次检查 interval,防止 sleep 期间用户切回手动时
仍然发起一次无意义请求
同时修复 DbStatusTab 和 ServerStatusTab 两处。
2026-07-01 17:09:39 +08:00
afa6bf892e
feat(register): 输入框支持回车键提交表单
...
注册页 4 个输入框(用户名/邮箱/密码/确认密码)原先 onkeydown 为 None,
无法用回车提交。改为监听 Enter 触发 on_submit,与登录页行为一致。
2026-07-01 17:00:58 +08:00
7de97d8222
fix(write): 编辑模式回显空白由 effect 竞态导致
...
编辑文章时编辑器空白。根因:edit_post 由 None→Some 时,回填 effect
(写 content signal)与初始化 effect(读 content signal)在同一 tick
触发,无保证先后顺序。初始化 effect 经常先跑,读到 content()= 跳过
setMarkdown,且 editor_content_set=true 永久阻止重试。
修复:初始化 effect 直接从 edit_post(真值源)取 content_md 回填,不再
依赖 content signal——后者是派生值,与回填 effect 存在竞态。两个 effect
都已在 edit_post 上订阅,回填时读取它没有竞态。
2026-07-01 17:00:58 +08:00
26d37f7271
refactor(admin): use FilterTabs in /admin/system
...
system.rs 的 tab 栏从 Tabs 改用既有 FilterTabs,视觉与评论页完全一致
(平滑滑动指示条 + 选中文字 text-paper-primary)。Props 形状相同,仅改组件名;
SystemTab::as_str/from_str 桥接与枚举 match 穷举逻辑不变。
2026-06-30 18:05:05 +08:00
bf91d18c4f
refactor(admin): use shared Tabs component in /admin/system
...
移除 System 组件内联的 tab 按钮,改调公共 Tabs 组件。
active_tab signal 经 SystemTab::as_str/from_str 与 Tabs 的 String API 桥接,
match 穷举面板选择逻辑不变。视觉/行为零变化,只消除重复。
同时移除 Task 1/2 加的临时 #[allow(dead_code)](as_str/from_str/Tabs 现已有调用方)。
2026-06-30 14:29:05 +08:00
ef75631889
feat(database): add SystemTab::as_str/from_str string bridge
...
为后续用基于 String 的 Tabs 公共组件替换内联 tab 做准备。
as_str/from_str 是纯函数双向映射,带 roundtrip/稳定 key/拒绝未知输入三类单测。
2026-06-30 14:13:54 +08:00
0b6a076d78
feat(database): use real COUNT(*) for small tables, fall back to estimate for large
...
row_estimate came from pg_class.reltuples, which is -1 for tables that
have never been ANALYZEd (the common case on a fresh/small DB), so the
admin table list showed -1 for every table — meaningless.
Replace row_estimate with row_count + row_count_estimated:
- total_size < 100MB: SELECT count(*) (real value, cost negligible on
small tables; identifier quoted via PG escaping to stay injection-safe)
- total_size >= 100MB: fall back to reltuples estimate, flagged so the UI
can prefix with ~
Frontend now shows real counts (1/2/14...) for small tables and ~N for
large ones; header copy updated to reflect the mixed source.
2026-06-30 13:57:37 +08:00
ecf3c73715
fix(database): make src/api/database compile under WASM (web-only) build
...
The /admin/system feature was never verified against the WASM frontend
target (dx serve builds src/main.rs with --features web and no server).
The entire src/api/database/ tree unconditionally pulled in server-only
crates (axum, sqlparser, dashmap, tokio, tokio_postgres, futures, regex)
and server-gated helpers (get_current_admin_user, parse_session_token,
get_user_by_token), so the frontend build failed with 43 errors.
Root cause + fixes (mirror the established settings.rs pattern):
- Gate server-only imports behind #[cfg(feature = server)] in
status/system_status/sql_console/schema/tasks/backup. The #[server] macro
strips call sites but NOT use statements, so unresolved imports remained.
- Ungate in main.rs: SystemSnapshot is a shared serde
type referenced by ServerStatus.host on both targets; only the sampler
task/SNAPSHOT static are internally server-gated.
- Gate (pure Axum handler, zero WASM consumers) and the
download_backup Axum handler in backup.rs.
- Move restore_backup's validation preamble (regex/backup_path/fs) inside
the #[cfg(feature = server)] block; gate std::path/chrono::Utc imports.
- Add [[bin]] required-features=[server] for generate_highlight_css so the
syntect-dependent build tool is skipped in web-only builds.
Genuine WASM bugs surfaced once the server bodies stopped masking them:
- system.rs: Closure import path (dioxus::prelude::wasm_bindgen ->
wasm_bindgen, matching write.rs); setTimeout expects i32 not u32;
editor_handle() -> editor_handle.read() (Signal is not Fn in 0.7).
- codemirror_bridge.rs: set_schema extern took &SqlSchema but SqlSchema is
a serde type with no IntoWasmAbi. Changed both set_schema signatures to
&JsValue; call site now serializes via serde-wasm-bindgen::to_value.
- system.rs: signals .set() inside spawn/use_future/onclick closures need
bindings (incl. the *_f rebinding copies); added cfg_attr
allow(unused_mut) on the 4 tab components, matching write.rs convention.
Verified: cargo check (default/server) clean; cargo check --features web
--target wasm32-unknown-unknown clean (0 errors); dx check clean; 411
cargo tests pass.
2026-06-30 13:41:23 +08:00
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
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
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
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
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
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
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
48a328f553
feat(admin): add decorative image to tiptap editor with focus-within fade effect
2026-06-29 15:45:24 +08:00
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
ae770cae3a
feat(ui): use EmptyState component for search no results
2026-06-29 14:14:03 +08:00
23f0cc6222
fix(ui): adjust search placeholder size and position
2026-06-29 14:10:21 +08:00
b366c00e74
fix(ui): enlarge and better center search placeholder image
2026-06-29 14:07:35 +08:00
b666472193
fix(ui): add margin between post count and list in tag detail page
2026-06-29 14:05:47 +08:00
3f7d112898
feat(pages): add empty placeholder image to search page
2026-06-29 13:55:08 +08:00
528a79fb83
feat(pages): add empty state to tags and tag detail pages
2026-06-29 13:50:05 +08:00
373498870a
style: apply cargo fmt to workspace
CI / check (push) Failing after 5m35s
CI / build (push) Has been skipped
2026-06-29 13:47:40 +08:00
5e1becf116
fix(admin): explicitly set pointer-events auto for trash settings panel to resolve click bug
2026-06-29 13:46:05 +08:00
ef8af77a08
style(admin): format trash.rs
2026-06-29 13:42:29 +08:00
fdaefae027
test: fix test_auto_purge_settings_has_transition_class false positive
2026-06-29 13:37:59 +08:00
768dbc9510
feat(admin): add smooth slide-down animation to trash auto-purge settings panel
2026-06-29 13:34:43 +08:00
a47a5a6ac9
feat(admin): update empty state for trash page
2026-06-29 13:26:45 +08:00
d91a56f8b2
refactor(admin): extract comments filter tabs into FilterTabs component
2026-06-29 13:15:50 +08:00
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
a1aef5bda9
fix(admin): add cursor-pointer to comment status filter buttons
2026-06-29 11:41:52 +08:00
dbe95f1173
feat(admin): use global EmptyState component in comments management page
2026-06-29 11:39:32 +08:00
72d21c6670
feat(admin): use global EmptyState component in posts management page
2026-06-29 11:38:50 +08:00
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
25e01a109b
fix(write): 移除 cover_uploading 多余的 mut 绑定
...
本组件只读取(on_submit 拦截)与传递给 CoverUploader,写入全在子组件
内部。Signal 是 Copy 类型,.set() 不要求 mut 绑定,编译器的 unused_mut
警告是真阳性。
2026-06-26 11:07:31 +08:00
c024de1b09
refactor(posts): 抽取 RebuildCacheBar 子组件,降低 PostsPage 复杂度
...
PostsPage 主体从约 181 行降至约 121 行。「重建内容 / 重建全部」工具条(两个按钮
+ 状态信号 rebuilding/rebuild_result + do_rebuild 异步闭包 + 结果消息)整体迁入
RebuildCacheBar 子组件,完全自洽,无任何 prop。
布局调整:原结果消息是页面级兄弟节点,现纳入子组件垂直容器(按钮行在上、消息
在下),保持「消息显示在按钮下方」的视觉语义;+ 写文章 链接仍与工具条在标题行
水平并排。
依据 dioxus-render-purity skill 的 god component 治理建议。验证:cargo build +
dx check + cargo test (406) 全绿。
2026-06-26 10:50:29 +08:00