21 Commits

Author SHA1 Message Date
xfy
64907401a0 fix(codemirror): 行号区背景与代码区割裂(core base theme 覆盖 catppuccin)
现象:行号列(gutter)背景是 #f5f5f5 浅灰,代码区(content)是 catppuccin
base #eff1f5 浅蓝灰,两者不一致,有明显割裂。

误诊修正:上一提交(e1b312e)以为是容器背景与 CodeMirror 层次扁平,
改了容器为 code-block(crust)。但 CodeMirror 内部 .cm-editor 和
.cm-gutters 都有不透明背景,容器色根本透不出来——所以「没效果」。
本次回退该容器改动。

真因:CodeMirror core 的内置 base theme(@codemirror/view dist index.js:6916)
给 `&light .cm-gutters` 设了 backgroundColor: "#f5f5f5" / dark "#333338",
特异性为 .cm-editor.cm-light .cm-gutters,高于 @catppuccin/codemirror
的 .cm-gutters 覆盖,catppuccin 的 base 背景被 core 默认灰压制。

修复:themes.ts 在 catppuccin extension 之后追加一个 EditorView.theme,
用 backgroundColor: transparent !important 强制 .cm-gutters 透明,
继承 .cm-editor 的 base 色,行号区与代码区完全融合。light/dark 同理。
2026-07-05 23:58:18 +08:00
xfy
e1b312e651 fix(admin): SQL 编辑器 gutter 与 content 背景割裂
现象:行号区和代码输入区背景色看起来不一致,有割裂感。

根因:三层背景色各不相同——容器用 paper-entry(mantle #e6e9ef)、
标题栏用 paper-theme(base #eff1f5)、CodeMirror 自带 base 背景。
容器与编辑器同处 base/mantle 之间,层次扁平,CodeMirror 的 base
浮不出层次,gutter 与 content 的细微差异被放大。

修复:建立 crust > mantle > base 三层明确递进——
- 容器改 paper-code-block(crust #dce0e8,最深)
- 标题栏改 paper-entry(mantle,中)
- CodeMirror 保持自带 base(最浅)

编辑器作为「浅色岛」浮在深色容器上(VS Code / Sublime 的 IDE
经典范式),gutter 与 content 同为 base 完全融合,割裂消失。
2026-07-05 23:10:56 +08:00
xfy
2cfc64b319 fix(admin): SQL 控制台 Ctrl+Enter 触发 panic(无 dioxus scope)
现象:编辑器有焦点时点击执行(或按 Ctrl+Enter)会 panic:
  called `Option::unwrap()` on a `None` value  (runtime.rs:223)
  RefCell already borrowed                       (runtime.rs:280)

根因:Ctrl+Enter 从 CodeMirror 的 JS 事件处理中触发 onRunShortcut
回调,此时 dioxus scope stack 为空。execute_for_editor 内部调
spawn(),而 spawn() 走 Runtime::with_current_scope →
current_scope_id().unwrap(),scope stack 空时直接 panic。
第二个 panic 是 unwind 期间再次 borrow scope_stack 触发的。

修复:用 dioxus 文档推荐的 web-sys 回调模式——在组件主体捕获
scope_id(此时 scope 活跃),JS 回调里用
Runtime::current().in_scope(scope_id, ...) 重建 scope 上下文
后再执行,spawn 即可找到 origin scope。

dioxus-core 源码(global_context.rs docstring)明示此模式,
dioxus-signals/src/global/mod.rs:201 也是这样用的。

注意:按钮 onclick 走 dioxus 事件系统(scope 已设),不受影响;
仅 CodeMirror JS 回调路径需要 in_scope 兜底。
2026-07-05 21:59:17 +08:00
xfy
fe6934ddd5 feat(admin): SQL 控制台接通 Ctrl+Enter 并重做视觉
接通上一提交打通的快捷键通道,同时重做编辑器/工具条/结果区视觉。

功能(接通 Ctrl+Enter):
- 把 run_sql 逻辑拆出 execute_for_editor 闭包(仅捕获 Copy 的 Signal),
  注入 CodeMirror onRunShortcut,按钮 onclick 仍走 run_sql
- 两条路径共用同一套 4 道护栏 + 资源逻辑,行为完全等价

视觉(编辑器标题栏一体化):
- 编辑器加标题栏:左 accent 状态点 + SQL 标识,右 ⌘↵ 快捷键提示
- 容器升至 rounded-2xl + 280px 最小高度,留呼吸
- 执行按钮改胶囊式(rounded-full)+ active:scale 触觉反馈 +
  SPINNER_SVG 替代纯文字「执行中...」
- 危险选项(我了解后果)用竖线分隔 + 红色语义,与普通选项分层
- 结果摘要从裸文本改徽章式(accent-soft 底 + 圆角胶囊)
- 加空结果友好态:SELECT 返回空集时显示「查询成功,无返回行」
- EXPLAIN 输出 pre 补 m-0,消除默认外边距
2026-07-05 21:31:16 +08:00
xfy
d3a50728ed style(admin): overhaul layout to modern minimalist sidebar design 2026-07-03 15:50:59 +08:00
xfy
38ba6692d3 style(admin): redesign admin UI with soft rounded corners and Catppuccin theme 2026-07-03 15:46:18 +08:00
xfy
d1041a84dd feat(admin): complete redesign of backend ui with industrial minimalist aesthetic 2026-07-03 15:32:02 +08:00
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
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