2b6b2ca9e4
fix(hooks): 修复 wasm32 target 编译失败
...
之前验证只跑了 server target(cargo build --all-features),漏掉 wasm32
前端 target,导致三类错误未被捕获:
1. event_listener.rs wasm 分支缺 use dioxus::prelude::*:
use_hook/use_effect/use_drop 来自 prelude,之前重写时误删。补回。
2. event_listener.rs use_effect 闭包 move 冲突(E0507/E0310):
use_effect 回调是 FnMut,但 acquire 是 FnOnce、handler 要 move 进事件
闭包。用 Option::take 模式包裹,首次运行消费,避免重复 move。同时给
A 加上 'static 约束。
3. query.rs items/total/footer.rs sync_visible 缺 mut:
wasm 分支的 .set() 调用需要 mut 绑定。之前为消 server target 的
unused_mut 警告误删,改用 cfg_attr 在非 wasm 时压制警告。
教训:fullstack 项目必须同时验证 wasm32 与 server 两个 target,单一
target 通过不代表整体可用。
2026-07-02 16:43:15 +08:00
291189fb77
fix(sanitizer): 折叠嵌套 if 修复 clippy::collapsible_if
...
865871f 引入的 checkbox type 白名单逻辑用了嵌套 if,触发 rust 1.96
的 clippy::collapsible_if,导致 make clippy 直接失败。按 clippy 建议
合并为单个 if 链,行为不变。
2026-07-02 16:36:05 +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
759bbab7ee
refactor(utils): comment_storage 从 hooks/ 迁至 utils/
...
该文件全是普通函数(save_author / load_pending_comments 等),没有任何
use_ 开头的 Hook,放在 hooks/ 下名不副实。内部只剩结构体与 localStorage
读写(时间/HTML 工具已在上一 commit 拆出),迁到 utils/ 后文件名与内容
自洽,hooks/ 目录回归只放真正的渲染期 Hook。
机械替换 4 个组件文件的 use 路径(hooks::comment_storage → utils::comment_storage)。
2026-07-02 16:18:52 +08:00
dcdd72a8c2
refactor(utils): 拆分 comment_storage 的时间/HTML 工具函数
...
消除 server 端对 hooks 模块的跨层引用:
- escape_html(+ 两份重复实现:comment_storage 用 '、helpers 用 ')
统一到 utils/html.rs,单引号采用 HTML5 标准 '。
- now_millis / relative_label_from_millis / format_relative_time_iso 及其
单元测试迁到 utils/time.rs,与既有 sleep_ms 同构(wasm32/非 wasm 双版本)。
调用方迁移:
- api/comments/helpers.rs:删掉本地 escape_html,format_relative_time 改引
utils::time;其 escape_html 测试随之删除(utils/html.rs 已覆盖)。
- api/comments/create.rs:两处 helpers::escape_html 改 utils::html。
- api/markdown.rs:escape_heading_text 改 utils::html。
- components/comments/pending_item.rs:format_relative_time_iso 改从
utils::time 引入;render_pending_content 留在 comment_storage(语义紧贴
待审核评论)。
- hooks/comment_storage.rs:is_expired 的 now_millis 改引 utils::time。
comment_storage.rs 现在只剩结构体与 localStorage 读写,文件名即将自洽。
2026-07-02 16:15:23 +08:00
f00c2bf963
refactor(hooks): 新增 use_event_listener 通用事件监听 hook
...
把 theme.rs 与 footer.rs 两处手写的 "use_hook 持有 Closure+target、
use_effect 注册 add_event_listener、use_drop 移除" 样板收口到一个
通用 hook,按 Dioxus 0.7 custom hooks 范式封装。
hook 设计:
- target 通过 acquire 闭包在 use_effect 首次运行时获取(此时 DOM 一定
可用),调用方无需再自己包 use_effect。
- 泛型 AsRef<EventTarget> 统一 Window / Element / MediaQueryList。
- 非 wasm32 整体编译为 noop,调用方用 #[cfg] 隔离 web_sys 调用即可。
footer 的 "注册后首次同步" 业务逻辑保留在 hook 之外(语义不属于
事件监听)。theme 的 handler 改为重新 match_media 读取 matches,
功能等价(低频事件,开销可忽略)。
2026-07-02 16:02:51 +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
865871f45c
fix(markdown): 修复任务列表 checkbox 在前台被 sanitizer 剥离
...
编辑器端已挂 TaskList/TaskItem,但前台渲染走 pulldown-cmark,其输出的
<input type="checkbox"> 不在 sanitizer 白名单内,被 remove_and_keep_content
剥离,导致任务列表在前台只剩裸 <li> 文本。
- sanitizer: 白名单放开 input,但强制 type="checkbox" 属性值校验
- attrs_to_remove 增加 input.type 值校验,非 checkbox 一律删除属性
- element_handler 末尾兜底:缺 type 或非 checkbox 的 input 整标签移除
(void 元素无内容,remove() 不丢正文,封堵 type=image/text 等 XSS 滥用)
- 仅 clean_html 放开,clean_comment_html 保持不放开(评论无需任务列表)
- input.css 补 .md-content 任务列表样式(pulldown-cmark 裸 li>input 结构,
用 :has() 识别,不支持 :has() 的浏览器降级为普通列表仍可读)
- 新增 sanitizer 5 个测试(白名单/XSS 边界)+ markdown 1 个端到端测试
2026-07-02 13:22:37 +08:00
a10003c90d
feat(theme): 切换按钮点击立即换图标并播放进入动画
...
将 theme.set 从 450ms 延迟改为立即执行——图标在点击瞬间切换,不再
等待圆形 VT 动画结束。原作者担心重渲染打断 VT 实属多余:VT 伪元素
快照在 __startThemeTransition 内已同步拍好,后续真实 DOM 变化不影响。
- 移除 click_gen 防抖机制与 spawn_local 延迟回调(不再需要)。
- SVG 加 key 属性,theme 变化时 Dioxus 重新挂载 SVG,触发 CSS 进入动画。
- input.css 新增 @keyframes theme-icon-enter(缩放 0.5→1 + 旋转 -60°→0°
+ 淡入,0.3s ease-out),仅在 .theme-toggle svg 上生效。
- prefers-reduced-motion 下禁用该动画,遵循项目既有约定。
2026-07-02 12:00:00 +08:00
9a4e6c3e6e
docs(agents): 校正构建/测试/迁移说明,对齐当前代码库
...
- 移除不存在的 ./migrate.sh:迁移实际由 src/db/migrate.rs 在启动期自动运行,
目标库由 db/pool.rs::ensure_database_exists 自举创建;补充新增迁移需在
MIGRATIONS 数组登记(编译期校验磁盘与数组一致)
- npm → pnpm:4 个 libs 均使用 pnpm-lock.yaml,Makefile 全程 pnpm
- make test / make build 覆盖 4 个 libs(补充 codemirror-editor)
- 补充 restore-webp 兜底步骤的成因(dx 0.7.9 重编码 .webp)
- 补全 make clippy(-D warnings)、make fix、make clean 真实行为
- 模块树补齐 context/router/ssr_cache/sysinfo_sampler 及 *_bridge
- 4 个前端子项目合并为对比表,保留 wasm-bindgen 接线等关键细节
- 保留 Workflow 提交规范
2026-07-02 11:40:47 +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
5fd883d7a1
feat(comments): 评论区有内容时自动调淡背景图透明度
...
与 tiptap 编辑器保持一致:无内容时 60%,有内容或聚焦时淡出到
10%。content_md 由 oninput 实时维护,输入即触发重渲染。
2026-07-02 11:14:58 +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
404f85b732
refactor(web): 用 document::Link 注入 favicon,移除手写 index.html
...
Dioxus 0.7 提供 document::Link 组件,可在 Rust 代码里声明 <link rel="icon">
注入 <head>,与已有的 document::Stylesheet / document::Title 同机制。
之前的方案手写 index.html(复制 dx CLI 默认 shell + 加 favicon 链接),
脆弱:嵌入了 toast 基础设施 JS/CSS,升级 dioxus-cli 后需手动同步。
改用 document::Link 后,favicon 由 SSR 运行时注入,无需维护静态模板。
2026-07-01 17:00:58 +08:00
afa6bf892e
feat(register): 输入框支持回车键提交表单
...
注册页 4 个输入框(用户名/邮箱/密码/确认密码)原先 onkeydown 为 None,
无法用回车提交。改为监听 Enter 触发 on_submit,与登录页行为一致。
2026-07-01 17:00:58 +08:00
78f3ff4b2f
fix(admin): 登出按钮补 cursor-pointer
...
登出按钮缺少 cursor-pointer,鼠标悬停时仍显示文本光标而非手型,与
其他可点击元素不一致。
2026-07-01 17:00:58 +08:00
3faff98dc8
fix(layout): 后台导航栏与正文宽度对齐
...
Header 的导航宽度写死 max-w-3xl(前台阅读宽度),后台直接复用导致
导航栏(768px)比正文(max-w-5xl=1024px)窄 256px,左边缘错位。
给 Header 加 max_width prop(默认 max-w-3xl 保持前台行为不变),后台
两处调用点传 max-w-5xl,与 admin_layout 的 main 同宽对齐。
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
90734446b5
refactor(ui): remove redundant Tabs component
...
Tabs 现已无调用方(/admin/system 已改用 FilterTabs),删除以消除 DRY 违反。
全局只保留 FilterTabs 一个 tab 组件,动画/样式统一。
2026-06-30 18:12:03 +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
5426458f4a
fix(docs): resolve rustdoc broken links and dead_code warnings
...
CI / check (push) Failing after 5m18s
CI / build (push) Has been skipped
doc 构建报 8 个 broken_intra_doc_links + dx 构建报 7 个 dead_code 警告。
两者根源相同:被引用的常量/类型/模块要么是 server-only(消费方都在
#[cfg(feature="server")] 块里),要么是跨 cfg 引用(host doc 构建看不到
wasm32-only 类型)。
dead_code:给 sql_console 的 MAX_ROWS/ABSOLUTELY_FORBIDDEN/GuardResult 与
backup 的 BACKUP_DIR/FILENAME_RE/BACKUP_SIGNATURE 加 #[cfg(feature="server")]
gate——这些都是安全护栏常量,消费方全在 server fn 体里,WASM 构建剥掉 fn 体后
常量即成死代码。sysinfo_sampler 的 WASM 桩 read_snapshot 加 allow(dead_code)
(与 codemirror_bridge 既有处理一致)。
rustdoc 链接:跨模块引用改全路径(crate::xxx),跨 cfg 引用(EditorHandle 在
wasm32 mod 内、host doc 构建不可见)转义为纯文本。
验证:cargo doc(0 warning)+ cargo check(default)+ wasm32 check 全部干净。
2026-06-30 17:42:52 +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
ff7d9e1e46
feat(ui): add Tabs component for page-level navigation
...
与 FilterTabs(列表筛选 + 滑动动画)并列,职责区分:
- FilterTabs:同质数据过滤(全部/待审/已通过)
- Tabs:切换到结构不同的面板(数据库/服务器/SQL 控制台)
选中态用按钮自身 border-b-2 下划线(无动画)。
API 与 FilterTabs 对齐(String value + EventHandler),便于调用方迁移。
2026-06-30 14:16:36 +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
36168f26d7
fix(database): cast extract(epoch)::double precision in active-connections query
...
extract(epoch FROM now() - query_start) returns numeric (decimal) in
PostgreSQL, but the result was read as f64 via r.get(4). tokio-postgres
has no FromSql<f64> impl for the numeric type, so get_db_status panicked
at runtime with 'error deserializing column 4' (HTTP 500).
Cast to ::double precision (float8) to match query_duration_secs: Option<f64>.
Audited the remaining column mappings in status.rs to rule out further
type mismatches — all sound: reltuples::bigint->i64, native bigint size
columns->i64, last_vacuum/last_analyze timestamptz->DateTime<Utc>
(chrono feature enabled), schema_migrations.version TEXT->String,
count(*)::int->i32 (fixed previously).
2026-06-30 13:51:48 +08:00
116286f80f
fix(database): cast count(*)::int in get_db_status to match i32 field
...
count(*) returns bigint (int8) in PostgreSQL, but the result was read as
i32 via conn_row.get(0). tokio-postgres's FromSql<i32> rejects an int8
column, so get_db_status panicked at runtime with
'error deserializing column 0' (HTTP 500).
Cast count(*)::int to int4, matching the adjacent setting::int column
and the total_connections/max_connections i32 struct fields. No other
queries in the file are affected: reltuples is already ::bigint -> i64,
size columns are native bigint -> i64, and system_status.rs reads its
count(*) as i64 correctly.
2026-06-30 13:47:24 +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
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
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
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
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
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
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
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
656e35c79f
fix(comments): move decorative image properly inside textarea background layer
2026-06-29 15:20:48 +08:00
0b53e968c1
feat(comments): add decorative image to comment input with focus fade effect
2026-06-29 14:42:03 +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
d1b5b8ee4e
fix(ui): suppress unused variable warning for active param in server build
2026-06-29 14:29:45 +08:00
ae770cae3a
feat(ui): use EmptyState component for search no results
2026-06-29 14:14:03 +08:00