|
|
b4e15ecbff
|
feat(admin): 文章列表支持按标题搜索
为 /admin/posts 的「全部文章」tab 增加按标题搜索能力:
- list_posts server fn 增加可选 search: Option<String> 参数,非空时按
title ILIKE 子串匹配(转义 % / _ / \,限长 200 字符)过滤草稿+已发布,
否则走原全量列表路径;COUNT 与分页查询同步带过滤条件。
- AllPostsList 增加搜索输入条(回车 / 搜索按钮提交,清除按钮复位),
search_query signal 在 use_paginated 的 page 闭包内同步读取以建立响应式
依赖:提交新词即使停在首页也会重新请求;fetch 闭包请求时读取当前词传后端。
搜索结果为空时展示「未找到匹配的文章」空状态。
- 同步更新 dashboard.rs 与 query.rs 文档示例中的 list_posts 调用签名。
|
2026-07-28 17:33:06 +08:00 |
|
|
|
8ea51cab7b
|
refactor(admin): 统一后台输入框为 FormInput 组件
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
扩展 FormInput:
- 新增 class 覆盖 prop(缺省 INPUT_CLASS,内联场景传 INPUT_INLINE_CLASS)
- 新增 mono flag(代码/表名/JSON 等等宽场景)
- 新增 INPUT_INLINE_CLASS 常量(flex-1 内联款,与按钮并排用)
迁移后台全部文本类输入框到 FormInput:
- write.rs:slug / 标签 / 封面 URL(内联 + 回车提交)
- asset_picker.rs / assets.rs:搜索框(flex-1 / w-56)+ alt 微调
- runner.rs:资源覆盖 JSON(mono)
- system/export.rs:表名(mono)
统一视觉语言(paper-entry/border/accent focus/rounded-2xl),
保留各场景必要布局差异(宽度、等宽、胶囊)。
保留为专用控件未迁移:标题大字、checkbox/radio、file、数字步进器。
|
2026-07-28 17:21:20 +08:00 |
|
|
|
1cb92a8046
|
refactor(mcp): 统一输入框为 FormInput 组件
- FormInput 的 disabled/onkeydown 加 #[props(default)],简化简单场景调用
- /admin/mcp 两处裸 input(名称、令牌明文)迁移到 FormInput
- 移除 mcp.rs 对 INPUT_CLASS 常量的直接依赖
|
2026-07-28 17:08:16 +08:00 |
|
|
|
a24107ab33
|
fix(mcp): 开发期放行 0.0.0.0 Host 让 dx 代理可用
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
dx serve --addr 0.0.0.0 转发到后端原生 server 时无条件把 Host 改写成
0.0.0.0:<随机端口>,rmcp 的 rebinding 防护据此一律 403,导致 MCP 在
dx 开发代理(:8080)下完全不可用。
仅当 APP_BASE_URL 未设或为 localhost 系(开发态)时把 0.0.0.0 加入
Host 白名单;生产域名态保持严格。直连原生端口始终可用(200)。
复现:8080 代理 →403,直连 56709 →200;显式发 Host:localhost 经
代理仍 403 证明 dx 无条件改写 Host。
|
2026-07-28 16:55:18 +08:00 |
|
|
|
6d520c3d48
|
fix(mcp): Host 白名单加入 APP_BASE_URL 域名
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
rmcp 的 DNS-rebinding 防护默认仅允许 localhost/127.0.0.1/::1,
生产反代 proxy_set_header Host $host 会把真实域名转发进来被 403。
现把 APP_BASE_URL 的 host 解析加入 allowed_hosts(保留默认值),
本地直连与生产域名同时放行。
注:本次只修服务端潜在 prod 403;用户当前 0.0.0.0 的 403 是客户端
配置误用了 bind 地址,应改用 localhost/域名。
|
2026-07-28 16:45:49 +08:00 |
|
|
|
cc88a7e90a
|
fix(mcp): 修正 Oh-My-Pi 配置格式与路径
三处错误(据 omp 客户端实际报错):
1. 字段名 transport→type,值 streamable-http→http:omp 不识别 transport,
遇未知字段退化为 stdio 因缺 command 报错丢弃
2. 路径 ~/.pi/agent/mcp.json→.mcp.json / ~/.omp/agent/mcp.json / ~/.mcp.json
3. omp 与 Claude Code 的 JSON 体现完全相同,差异仅在配置文件路径
|
2026-07-28 16:39:43 +08:00 |
|
|
|
3f3bf411ca
|
feat(mcp): 添加 OpenCode 客户端配置(第二位)
OpenCode 用 opencode.json(非 mcp.json),根键 mcp(非 mcpServers),
远程端点 type: remote(非 streamable-http),带 $schema 与 enabled 字段。
来源:2026 opencode.ai 官方文档。同步更新骨架屏形状数组(0..7)。
|
2026-07-28 16:34:15 +08:00 |
|
|
|
9d7f01a4ce
|
feat(mcp): 令牌列表添加骨架屏
TokenList 首次加载时(loading 且无数据)用 DelayedSkeleton 包裹的
骨架表格占位,4 行 7 列对齐真实表格列宽;加载完成后才区分空态/表格。
修复了首次加载时误显「暂无令牌」的问题。
|
2026-07-28 16:19:36 +08:00 |
|
|
|
2d995d6eaa
|
refactor(mcp): Oh-My-Pi 配置移至首位
将 Oh-My-Pi 客户端配置排在第一,同步调整骨架屏形状数组顺序。
|
2026-07-28 16:15:23 +08:00 |
|
|
|
ee1732214e
|
feat(mcp): 配置生成中 loading 替换为骨架屏
用 SkeletonBox 组合出 6 个配置片段的占位结构(标题行 + 代码块),
高度按真实 JSON/CLI 配置长短变化,比纯文本「生成配置中…」更直观。
|
2026-07-28 16:11:42 +08:00 |
|
|
|
070d06dacd
|
fix(mcp): 复制按钮添加就地反馈
Toast 渲染在页面顶部,配置区在页面最末,点击复制时 Toast 落在视口外看不到。
为每个 ConfigSnippet 复制按钮加本地 copied 信号:点击后按钮就地短暂变绿
提示「已复制」1.5s(经 sleep_ms),不依赖滚动位置。
|
2026-07-28 16:05:09 +08:00 |
|
|
|
1b772f4534
|
feat(mcp): 客户端配置代码块添加语法高亮
将配置片段 DTO 从扁平字段重构为 snippets Vec(含 content/content_html),
高亮在服务端经 highlight_code 完成(JSON 配置用 json 语法,CLI 用 bash),
前端以 dangerous_inner_html 渲染并包 .md-content 匹配 highlight.css 作用域。
补 json/bash 语法解析测试,确认两者均产出彩色 span 而非纯文本回退。
|
2026-07-28 15:53:20 +08:00 |
|
|
|
53376a0373
|
fix(mcp): 修正 Claude Code / Cursor 客户端配置格式(联网核实 2026)
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
依据 2026 官方文档联网核实所有 MCP 客户端配置格式,发现两处错误:
1. Claude Code .mcp.json:type 值应为 "http",不是 "streamable-http"。
Claude Code 用 streamable-http 会静默失败/卡在 connecting 状态。
2. Cursor ~/.cursor/mcp.json:不应带 type 字段——Cursor 按远程 URL 自动
识别 streamable-http,仅需 url + headers。此前 cursor_json 直接
clone claude_code_json,继承了错误的 type 字段。
其余配置核实无误:Cline(streamableHttp 驼峰 + disabled/autoApprove)、
Claude Code CLI(--transport http --header)、Oh-My-Pi(transport 字段)。
改动:
- 拆分 cursor_json,不再 clone claude_code_json
- 更新对应 doc 注释(config.rs / mcp_tokens.rs DTO)
- claude_code 测试断言改为 type=="http"
- cursor_json_equals_claude_code_json → cursor_json_has_no_type_field
|
2026-07-28 15:10:01 +08:00 |
|
|
|
78fb51a310
|
feat(mcp): 客户端配置新增 Oh-My-Pi 片段
在 /admin/mcp 客户端配置展示区增加 Oh-My-Pi(omp)的 MCP 配置片段,
位于 Cline 与「通用」之间。
Oh-My-Pi 的 mcpServers 条目与 Claude Code/Cursor 的关键差异:字段名
是 transport(而非 type),值仍为 streamable-http,auth 同样走
headers.Authorization。配置文件落在 ~/.pi/agent/mcp.json(全局)或
.pi/mcp.json(项目级)。
改动:
- ClientConfigs / McpClientConfigs 新增 omp_json 字段
- generate_client_configs 生成 omp 片段
- ConfigCard 增加 ConfigSnippet 行
- 新增测试 omp_json_uses_transport_field_not_type,并纳入 pretty 校验循环
格式依据:2026 pi.dev / oh-my-pi 官方文档(联网核实)。
|
2026-07-28 15:04:33 +08:00 |
|
|
|
063ad6e606
|
fix(mcp): 修复 ConfigCard use_effect 死循环
use_effect 内读 loading() 作为重入 guard,使 loading 成为响应式依赖:
effect 设 loading=true → loading 变化触发 effect 重跑 → 早退;
fetch 完成设 loading=false → loading 变化再次触发 effect → 再次跑,
无限循环,导致 /api/get_mcp_client_configs 被疯狂调用。
use_effect 本身按依赖去重,config_token 不变就不会重跑,guard 多余;
删掉 effect 内的 loading() 读取,loading 不再是依赖,循环解除。
(on_manual_input 内的 loading 读取在事件闭包里,不构成响应式订阅,保留。)
|
2026-07-28 14:57:40 +08:00 |
|
|
|
1d1b32cdd2
|
fix(admin): FormSelect 面板水平居中于窄触发器,修复双倍位移
紧凑触发器(如「手动」73px)下面板仅左对齐,宽出部分全堆在右侧,
视觉失衡。面板改为 left-1/2 + translateX(-50%) 以触发器中心居中,
宽出部分两侧对称探出。
两个 CSS 陷阱及其解法:
- select-enter 关键帧的 transform 会覆盖静态 transform,fill 结束时
位置跳变 → translateX(-50%) 烘进关键帧首尾(同 popover-enter 先例)
- Tailwind v4 的 -translate-x-1/2 utility 走独立 translate 属性,与
关键帧的 transform 属性叠加成双倍位移 → 面板改用任意属性类
[transform:translateX(-50%)]
浏览器实测:紧凑面板中心偏差 -0.13px、胶囊面板 0.00px(动画播完后测量)。
|
2026-07-28 14:53:59 +08:00 |
|
|
|
60360f6b49
|
fix(mcp): reveal/revoke 令牌时将字符串 id 解析为 Uuid
reveal_mcp_token / revoke_mcp_token 把前端传来的 String id 直接绑到
$1::uuid,tokio-postgres 解析参数类型为 uuid 后拒绝 String,报
'cannot convert between the Rust type String and the Postgres type uuid'
导致 /api/reveal_mcp_token 500。
参照 assets/delete.rs 的做法,绑定前用 uuid::Uuid::parse_str 转换;
解析失败按令牌不存在处理(reveal 返回 None、revoke 静默无操作),
与函数既有的'避免探测'语义一致。
|
2026-07-28 14:42:14 +08:00 |
|
|
|
e6f7704367
|
feat(admin): 全项目原生 <select> 统一替换为 FormSelect
将剩余 4 处原生下拉(write 编辑器状态胶囊、db_status/server_status
自动刷新、export 导出格式)全部迁移到主题化的 FormSelect:
- FormSelect 新增 trigger_class 可选 prop(缺省全宽表单款),并导出
FORM_SELECT_COMPACT_CLASS 供工具栏内联小下拉复用;write.rs 状态选择器
以胶囊 trigger_class 复刻原外观(面板贴底栏自动上翻,替代原
「隐形 select 覆盖 span」hack)
- 面板宽度改为 max(触发器, 最长选项)(w-max min-w-full + 100vw 上限),
紧凑触发器(如「手动」)下长选项(如「500ms」「SQL (INSERT)」)
不再被截断
- db_status/server_status 的字符串 match 反查收编为类型安全选项表
(Option<u32> 直进直出)
- 因 system/write 页面 SSR 真实引用 FormSelect,移除 FORM_SELECT_ID /
wrap_index 上不再需要的 #[allow(dead_code)]
src 下已无任何原生 <select> 元素。浏览器实测:紧凑面板 93.8px 宽于
触发器 73.8px、胶囊面板上翻贴顶 6px 间隙、选中 check 与高亮行正确。
|
2026-07-28 14:38:12 +08:00 |
|
|
|
717d17d5a7
|
feat(admin): FormSelect 自定义主题化下拉弹层,替代原生 select 弹窗
原生 <select> 的弹出列表由 OS/浏览器渲染,暗色主题下是白底系统菜单,
无法跟随 Catppuccin 主题。将 FormSelect 内部重写为
button[aria-haspopup=listbox] + 绝对定位面板(组件签名不变,调用方零改动):
- 面板/选项全部使用语义色 token,与 POPOVER_PANEL_CLASS 同源
(卡片化圆角 + 阴影),新增 select-enter 入场动画(150ms,
含 prefers-reduced-motion 豁免)
- 键盘:focus 留在触发器,aria-activedescendant 高亮;↑↓ 循环
(wrap_index)、Enter/Space 选中、Esc 关闭、Home/End 跳首尾、
Tab 关闭并自然流转焦点;选项 onmousedown 阻止默认行为避免抢夺焦点
- 透明遮罩拦截外部点击关闭(同 Popover 模式)
- 打开时按视口空间决定展开方向(should_flip + measure_flip,
下翻/上翻分别设 transform-origin)
- 打开/键盘导航时高亮项 scrollIntoView(block: nearest)
- 选中行:text-paper-accent + check 图标;高亮行:accent-soft 背景
should_flip / wrap_index 抽为纯函数并配单测(边界:恰好放得下、
上方更窄不翻、空列表回绕安全)。
|
2026-07-28 14:19:52 +08:00 |
|
|
|
8b4a2b674e
|
feat(admin): 封装 FormSelect 组件并修复 MCP 页下拉箭头错位
/admin/mcp 的两个 <select> 直接套用文本框的 INPUT_CLASS,原生下拉箭头
由浏览器绘制,深色主题下位置/颜色不协调(垂直不居中、无法跟随主题色)。
- components/forms.rs 新增泛型 FormSelect:appearance-none 隐藏原生箭头,
右侧叠 pointer-events-none 的 chevron SVG(top-1/2 -translate-y-1/2
精确居中);option value 内部用序号占位,onchange 直接回传选中项的 T,
调用方免去「字符串反查枚举」样板
- mcp.rs 两个下拉改用以简形式传入 SCOPE_OPTIONS / LIFETIME_OPTIONS
浏览器实测:chevron 垂直居中偏差 0.00px,右间距 16px(与左内边距对称)。
|
2026-07-28 13:52:28 +08:00 |
|
|
|
ef4c4ee8ed
|
docs(mcp): CHANGELOG + .env.example + AGENTS.md + DEPLOYMENT(T7)
- CHANGELOG:[Unreleased]/Added 记录 MCP 服务器功能(传输/认证/作用域/工具面/加固/配置/依赖/环境变量)。
- .env.example:新增 MCP_TOKEN_ENC_KEY(必填,openssl rand -hex 32)+ RATE_LIMIT_MCP_PER_SEC/_BURST。
- AGENTS.md:新增「MCP Server」章节(传输/认证/令牌存储/作用域/工具组合模式/feature 门控/配置生成)+ Key Directories 加 src/mcp/ + 迁移数 14→17。
- DEPLOYMENT.md:必设变量表加 MCP_TOKEN_ENC_KEY;新增「/mcp 端点」反代说明(catch-all 代理/Origin 白名单取自 APP_BASE_URL/bearer 不经 cookie/独立限流桶)。
实时客户端冒烟测试延后到用户环境(需运行中的 DB + MCP_TOKEN_ENC_KEY + 真实 MCP 客户端);
机制已由 T1 探针验证,集成路径在工单 T7 记录了用户侧步骤。
|
2026-07-28 12:01:23 +08:00 |
|
|
|
45356c3766
|
feat(mcp): 端点加固 — token 限流、last_used_at 节流、审计日志(T6)
- token-keyed 限流:MCP_LIMITER 按 token_id 计数(与 web 的 IP-keyed 限流隔离),
默认 10/s burst 30,经 RATE_LIMIT_MCP_PER_SEC/_BURST 可调,超限返回 429。
- last_used_at 节流:仅在 NULL 或距上次 ≥ 60s 时写库,避免高频请求每次 UPDATE。
resolve_principal 查询带出 last_used_at 做窗口判断。
- 审计日志:鉴权中间件记录每次已认证 MCP 请求(user_id/scope/token_id),
为自主 AI 客户端的活动留下事后追溯痕迹。
- 输出清理:经设计保证——JSON 序列化转义控制字符,搜索结果硬上限 50,
rmcp 默认 4MiB 请求体上限兜底。间接提示注入靠作用域隔离 + 客户端人工审批。
验证:clippy --all-features -D warnings 干净;659 单测 + 1 集成全绿;web/server 双目标编译通过。
|
2026-07-28 11:57:00 +08:00 |
|
|
|
9eac025b3e
|
feat(mcp): 完整工具面 + token 管理 + 管理页(T2-T5 并行实现 + 合并)
T2 token 管理(src/api/mcp_tokens.rs):create/list/reveal/revoke + get_mcp_client_configs
服务端函数,均经 get_current_admin_user 鉴权;TokenLifetime 枚举(1/7/30/90天/永不过期)。
src/mcp/config.rs 生成 4 种客户端配置(Claude Code/Cursor/Cline/通用 + CLI)。
src/pages/admin/mcp.rs 令牌管理页:列表 + 新建表单 + 一次性明文展示/重查 + 撤销 +
配置片段卡片。路由 /admin/mcp,导航项加入 admin_layout。
T3 read 工具 + Resources(tools/read.rs, resources.rs):search_posts/get_post/list_tags
(read 作用域,仅已发布);published-post Resources(post://{slug},游标分页)。
search_published 提取为共享函数。
T4 write 工具(tools/{posts,comments,tags,media}.rs):create/update/publish/trash/delete_post、
评论审核、标签 CRUD、媒体 base64 上传(WebP 转码 + 去重)。复用既有 helper 的 SQL 与
缓存失效(moka + ssr_cache)。write 作用域,可读草稿。
T5 admin 工具(tools/{settings,runner}.rs):get/update_settings、run_code(沙箱执行)。
admin 作用域。
合并(server.rs):用 rmcp tool_router 的「命名路由 + 组合」模式,7 个工具组在
YggMcpServer 上 impl,combined_router 用 + 合并成单一 ServerHandler。各工具组鉴权
独立,经 Extension<Parts> 读 McpPrincipal + scope.grants 校验。
新增依赖 base64 0.22(媒体上传解码)。验证:server/web 双目标编译通过、clippy
--all-features -D warnings 干净、659 单测 + 1 集成全绿(+20 新测试)。
|
2026-07-28 11:53:32 +08:00 |
|
|
|
7b9dfa7851
|
feat(mcp): tracer bullet — /mcp 端点骨架与 search_posts 工具
T1 探针式实现,接通端到端链路并验证双目标编译:
- 依赖:rmcp=3.0.0-beta.3(server/macros/transport-streamable-http-server/
transport-worker)+ aes-gcm,均经 server feature 门控。
- 迁移 017_mcp_tokens:UUID 主键、scope 约束、token_enc 密文 + token_hash
唯一索引(仅未撤销),users.id 外键级联。
- 模型 src/models/mcp_token.rs:TokenScope(read<write<admin 偏序,grants)+
McpToken/McpTokenSummary/CreateTokenResponse DTO。
- src/mcp/(server-only):
- crypto.rs:AES-GCM-256 加解密(hex 密钥、每次独立 nonce、8 项单测)。
- auth.rs:bearer→SHA-256→DB 常量查找→注入 McpPrincipal,刷新 last_used_at。
- server.rs:rmcp ServerHandler,search_posts 工具(read 作用域,pg_trgm 查询)。
- router.rs:StreamableHttpService(无状态+JSON 响应+Origin 白名单)挂载 /mcp。
- main.rs:merge mcp_route 到应用路由;models/mod.rs 注册 mcp_token 模块。
验证:rmcp+axum-0.8 兼容性经临时探针实测(tools/list、tools/call、Origin→403、
bearer→工具 principal 全链路);--features web 与 --features server 均编译通过;
clippy --all-features -D warnings 干净;639 单测+1 集成 +13 新 MCP 测试全绿。
|
2026-07-28 11:09:55 +08:00 |
|
|
|
801cb79e0d
|
docs(mcp): 新增 MCP 服务器调研与规格
调研(docs/mcp-research.md):对照 2025-11-25 spec 与 rmcp 源码,确认
Streamable HTTP 是 2026 唯一远程传输、stateless 为默认、bearer token 为
单租户务实方案、官方 rmcp 的 Tower StreamableHttpService 可挂载于 axum。
规格(docs/mcp-spec.md):锁定 9 项设计决策(受众/作用域/有效期/检索机制/
工具面/草稿可见性/加密存储/配置目标/构建路径)、模块划分、迁移、验收标准。
rmcp 锁定 =3.0.0-beta.3(稳定版 0.2.1 缺 Origin 校验等 spec 强制项)。
|
2026-07-28 11:09:45 +08:00 |
|
|
|
f6e1dff600
|
perf(admin): 放慢仪表盘动画节奏
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
- 卡片入场 400→600ms(内联 animation-duration 覆盖),阶梯延迟 0/60/120/180→0/120/240/360ms
- 近期文章行 row-enter 300→450ms,stagger 步进 50→80ms
- CountUp 滚动时长 500→900ms
|
2026-07-27 14:56:00 +08:00 |
|
|
|
61634882c5
|
feat(admin): 仪表盘添加分块进场动画与数字滚动
- StatCard 三张卡 animate-page-enter 阶梯入场(0/60/120ms),待审评论卡数据就绪后补挂入场类(180ms),近期文章行 animate-row-enter 逐行 stagger(50ms 步进)——动画挂在数据就绪的真实分支上,避免骨架屏截断
- 新增 CountUp 组件:数值 easeOutQuint 滚动(500ms),use_effect 内驱动保持渲染纯净;命中 prefers-reduced-motion 直接显示终值
- input.css 新增 row-enter 关键帧;reduced-motion 媒体查询补全 animate-page-enter/popover-enter/row-enter 覆盖(此前仅 page-enter 生效)
- BTN_SECONDARY 补 active:scale-[0.98] 按压反馈,与 BTN_PRIMARY 对齐
|
2026-07-27 14:39:10 +08:00 |
|
|
|
5743c7a581
|
feat(upload): 上传图片按内容 SHA-256 去重,重复上传复用已登记素材
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
assets 新增 content_hash 列(唯一索引,迁移 016;存量行保持 NULL
不参与去重)。上传在安全性校验后、转码前对原始字节算 SHA-256:
命中即刷新 created_at/updated_at(重启 7 天清理保护窗)并返回
已有 URL,跳过 GIF/WebP 验证、转码与落盘。并发同内容上传由唯一
索引 + INSERT ... ON CONFLICT DO NOTHING 兜底,落败者删落盘文件
复用胜出者路径。命中响应带 reused: true。仅精确去重,视觉相似
检测(pHash)有意不做。
|
2026-07-27 13:25:26 +08:00 |
|
|
|
775c58a9e6
|
feat(admin): 素材页支持多选与批量删除
卡片勾选框:仅未引用素材可选(与单删保护语义一致),hover 显现、
有选择时常显,选中卡片加主题色 ring;stop_propagation 防触发灯箱。
批量条:已选计数 / 全选本页(可切换取消本页) / 清除 / 删除所选
(两步确认),选择跨翻页与筛选保留。
服务端新增 BatchDeleteAssets:一次查询取引用存在性,被引用的跳过,
其余删文件(NotFound 容忍)+ 批量删 DB 行 + 清派生缓存,返回
删除/跳过/释放/失败统计。
|
2026-07-27 11:40:02 +08:00 |
|
|
|
248060d920
|
feat(admin): 素材页搜索框添加 300ms 防抖
query 仍作输入框受控绑定,debounced_query 才是请求参数:停顿
300ms 无新输入才提交,击键重启 effect 使旧延时任务经 peek 比对
后静默过期。页码重置移入防抖提交点——放在 oninput 里会让 page
变化用旧关键词抢发一次请求。
|
2026-07-27 11:29:28 +08:00 |
|
|
|
0086f0f3ea
|
feat(admin): 素材卡片「复制」更名「复制路径」
按钮复制的是 /uploads/ 相对路径而非完整 URL,名称如实反映行为;
title 同步为「复制图片相对路径」。
|
2026-07-27 11:23:57 +08:00 |
|
|
|
d610fd5837
|
feat(admin): 素材页「孤儿」更名为「未引用」
与「引用中」对仗更自然。覆盖统计行、筛选 tab、清理按钮、
清理结果横幅、卡片徽章及 purge 服务端消息;代码标识符
(orphan_count / purge_orphan_assets 等)保持不变。
|
2026-07-27 11:16:59 +08:00 |
|
|
|
a890257009
|
feat(admin): Pagination 组件支持页码跳转
当前页码渲染为可编辑输入框(仅回调式翻页且总页数 > 1 时):
聚焦编辑、回车跳转(自动夹取到合法范围,非法输入还原)、失焦回显。
posts / assets / trash 三个 signal 翻页页已接入 on_jump;
路由式翻页(comments、前台)不传 on_jump,页码保持纯文本。
|
2026-07-27 11:13:26 +08:00 |
|
|
|
e33e89c258
|
fix(assets): 工具栏右侧按钮沉入 FilterTabs 的 mb-6 区域紧贴横幅
工具栏 items-end 对齐的是 flex 项的 margin box:FilterTabs 带 mb-6 使容器
底端低于下划线 24px,右侧按钮组无底 margin 被一并拉到底端,距横幅仅 4px。
按钮组同加 mb-6(移除补偿用的 pb-1)后,按钮底边与 tabs 下划线齐平,
下划线→横幅、按钮→横幅、横幅→网格均为 24px。
|
2026-07-27 10:01:56 +08:00 |
|
|
|
b4bea7db0d
|
fix(assets): 操作横幅上下间距不对称(40px/8px → 24px/24px)
FilterTabs 自带 mb-6 在 flex 工具栏内撑出 24px,再叠横幅 mt-4 形成 40px
空洞,而横幅下方仅网格 mt-2 的 8px。横幅改 mt-4 为 mb-6:上方回归 tabs
标准间距 24px,下方与网格 mt-2 margin 塌陷同为 24px,与 comments/system
页节奏一致。
|
2026-07-27 09:57:15 +08:00 |
|
|
|
55e4712f28
|
fix(assets): 删除返回「素材不存在」时刷新网格自愈过期数据
行已被其他会话/重建删除时,本页网格仍显示幽灵卡片(success:false 不触发
reload)。refs 为空的业务拒绝即「行已不在 DB」,此时同样 bump reload,
让网格收敛到服务端真实状态;引用中拒绝(refs 非空)不误刷新。
|
2026-07-27 09:46:46 +08:00 |
|
|
|
b98059edcc
|
fix(assets): 引用徽标加 z-10 修复被 blur-img 展示层遮盖
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
灯箱改造(051ca94)把缩略图换成 .blur-img 双层结构后,.blur-img-full
自带 z-index:1,而徽标 span 是 z-index:auto,绘制序被图片压过,
导致「孤儿 / 被 N 篇引用」徽标在 DOM 中存在但不可见。
浏览器实测:修复前 elementsFromPoint 命中 blur-img-full,
修复后命中徽标本身,琥珀色孤儿药丸恢复显示。
|
2026-07-24 18:31:45 +08:00 |
|
|
|
99d33b54d2
|
fix(lightbox): 图集切换不重算几何导致图片压扁
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
gotoIndex 换图时只替换 src,target/baseW/baseH/transform 全部沿用
第一张图的几何,宽高比不同的图被套用错误的 scale(显示宽高比 =
自身宽高比 × 首图宽高比)。同时 .lightbox-img 的 object-fit: contain
与 transformFor 依赖的「位图填满布局盒」前提冲突,素材网格的方形
缩略图容器下首图即被压扁。
修复:
- swap() 在新图加载后按 naturalWidth/Height 重算 fitCentered 目标,
布局盒设为 target 尺寸、transform 归位 scale(1,1),baseW/H 同步
供关闭飞回动画复用
- 先换 src 再判 complete,避免用旧图的 complete 误判缓存命中
- 移除 .lightbox-img 的 object-fit: contain,恢复位图填满布局盒
新增回归测试:切换后几何必须按新图宽高比重算(红绿验证通过)
|
2026-07-24 18:15:11 +08:00 |
|
|
|
051ca947e3
|
feat(assets): 素材网格接入灯箱预览
- 缩略图改为与前台正文图一致的 .blur-img 双层结构(?w=20 占位 +
?thumb=300x300 展示层 data-src),点击由全局注入的 lightbox.js 接管;
图集模式(当前页内 ←/→ 切换),灯箱加载原图(data-src 去 query)。
- 数据异步到达/筛选/翻页/刷新后由 use_effect 调 __initLightbox 重新绑定
(双保险契约同 PostContent:__lightboxSelectors + 直接调用兜底)。
- lightbox.ts 补 data-lb-bound 幂等守卫:Dioxus keyed diff 复用节点时
重复初始化会叠加 click 监听(点一次开两次);附回归测试。
- invoke_optional_global 提取为 src/utils/js.rs 共享,post_content 复用。
浏览器实测:47 张绑定齐全,原图打开/图集切换/Esc 关闭正常,
筛选重建与重建索引重取后重绑幂等(单次点击仅 append 一次 overlay)。
|
2026-07-24 17:39:34 +08:00 |
|
|
|
fa40f4bd95
|
fix(assets): uuid 参数序列化失败导致上传 500
tokio-postgres 将 $1::uuid 的参数推断为 uuid 类型,String 无法
序列化(缺类型桥接),INSERT assets 报 error serializing parameter 0,
补偿删文件后返回 500。list/delete/purge/rebuild 的 $1::uuid 与
ANY($1::uuid[]) 全是同一颗未引爆的雷。
正路:tokio-postgres 启用 with-uuid-1,SQL 侧去掉全部 ::uuid cast,
Rust 侧全程 Uuid 类型,仅在 serde DTO 边界(WASM 共享模型)转 String;
外部传入的 id 在边界 parse_str,非法 id 走业务错误而非 500。
|
2026-07-24 17:04:35 +08:00 |
|
|
|
8f75a64bfd
|
fix(assets): SUM(size_bytes) 显式转 bigint 修复列表 500
Postgres 的 SUM(bigint) 返回 NUMERIC,tokio-postgres 反序列化为
i64 时类型不匹配,row.get(3) 直接 panic(release 的 panic=abort
下会崩掉整个进程)。COALESCE(..., 0)::bigint 修正。
|
2026-07-24 16:50:30 +08:00 |
|
|
|
45d70e1dc6
|
refactor(assets): 清理 lint,对齐项目惯例
- api/assets/mod.rs 按 posts/comments 惯例加
#![allow(clippy::unused_unit, deprecated, unused_imports)]
(#[server] 宏固有噪音)
- 页面交互导入按 target_arch = wasm32 门控;组件级放行非 WASM 未用绑定
- 合并 check_upload_dimensions 进 upload_dimensions(单函数返回尺寸,
消除死代码),测试同步更名
|
2026-07-24 16:44:47 +08:00 |
|
|
|
5612f16425
|
feat(assets): 封面「从素材库选择」联动
- AssetPickerModal:素材网格(最新 60 + 搜索)单击回填封面 URL,
内嵌「上传新图」(复用 upload_image_file,成功即选中,
失败留在 modal 内提示),上传期间置位 cover_uploading 拦截保存
- CoverUploader 空态加「素材库」入口(与「输入链接」并列)
|
2026-07-24 16:32:43 +08:00 |
|
|
|
0c47e87d30
|
feat(assets): 全量重建索引
- rebuild_assets_index:扫 uploads/(跳过点目录)→ upsert assets
(xmax=0 判别新增;技术字段 IS DISTINCT FROM 才更新,保留 alt,
幂等重跑 updated=0)→ 删文件已消失的行 → 全表扫 posts 重建 refs
- 磁盘扫描与 header 尺寸读取移入 spawn_blocking
- extract_asset_paths 提升 pub(crate) 供重建复用同一提取逻辑
- 前端「重建索引」按钮(进行中禁点,结果进横幅)
|
2026-07-24 16:27:02 +08:00 |
|
|
|
799455da43
|
feat(assets): 删除保护、孤儿清理与 alt 编辑
- delete_asset:引用中业务拒绝(Ok(success:false) + 引用文章列表),
孤儿硬删除(文件 + DB 行级联 refs + 派生缓存)
- purge_orphan_assets:一键清理无引用且超过 7 天保护窗的素材,
容忍单项文件删除失败,返回数量/释放字节/失败数
- update_asset_alt:管理性 alt 编辑
- invalidate_asset_caches:IMAGE_CACHE 前缀批量失效 + 尺寸缓存失效;
磁盘派生缓存(哈希命名不可枚举)由既有清理任务回收
- 前端:卡片操作区三态(复制/alt/删除 ↔ alt 编辑 ↔ 确认删除),
顶栏「清理孤儿」两步确认(带数量与总大小),操作结果横幅
|
2026-07-24 16:23:00 +08:00 |
|
|
|
99b96e7a0c
|
feat(assets): 素材管理只读列表页
- list_assets:引用状态筛选 + filename/alt 搜索 + 时间/大小排序 +
分页(60/页),一次响应带齐 tabs 计数与可清理孤儿统计
- /admin/assets 网格页:?thumb=300x300 缩略图卡片 + 引用徽标,
单路由客户端分页(对齐管理文章模式),导航加「素材」入口
|
2026-07-24 16:15:19 +08:00 |
|
|
|
0c811efd4d
|
feat(assets): 素材注册表数据层
- migration 015:assets 元数据注册表 + asset_refs 引用关联表
- upload_image 落盘后登记 assets(失败补偿删文件);尺寸校验复用
upload_dimensions 一次解析同时返回 (w,h)
- sync_asset_refs 镜像 sync_tags:文章 create/update 事务内
解析 content_html + cover_image 的 /uploads/ 路径重建引用
- Asset/AssetDto/AssetFilter/AssetSort 共享模型,id 以 String 承载
(SQL 侧 ::uuid cast,避免 uuid crate 进 WASM 构建)
|
2026-07-24 16:07:07 +08:00 |
|
|
|
adde92e15c
|
docs(assets): 素材管理功能设计规格
grilling 访谈确认的结论落盘:assets/asset_refs 双表、
sync_tags 模式引用同步、引用中禁删 + 孤儿 7 天窗一键清理、
手动重建索引回填、封面联动(编辑器正文选择器不做)。
|
2026-07-24 15:26:56 +08:00 |
|
|
|
de4bfafb72
|
chore: release v0.6.2
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
v0.6.2
|
2026-07-24 14:02:10 +08:00 |
|
|
|
2b048b94a3
|
fix(utils): sleep_ms 的 cfg guard 对齐 feature 模型,修复 web-only 构建失败
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
sleep_ms 原用 #[cfg(not(target_arch = "wasm32"))] guard tokio::time::sleep,
但 tokio 是 server-only 的 optional 依赖。此 guard 会在「非 wasm32 主机 + 仅
web feature」组合(如 cargo build --no-default-features --features web)下
误激活,此时 tokio 未引入,导致编译失败。
该 bug 长期被 [dev-dependencies] tokio 掩盖——cargo test / 开发构建总能拿到
tokio,只有发布/生产构建(排除 dev-deps)才暴露。符合 AGENTS.md 第 1 条
dual-target gating 标准:触及 server-only 依赖的代码必须用 #[cfg(feature = "server")]
而非 target_arch 区分。
修复:tokio 版本改为 #[cfg(all(feature = "server", not(target_arch = "wasm32")))],
并为无效构建组合「非 wasm32 且非 server」补一个 panic stub(该组合不是有效部署
目标,web feature 真实构建目标即 wasm32;stub 仅保证符号可编译,运行时不会触发)。
调用点 runner.rs 的 server 占位分支(通过 sleep_ms 继承同一问题)无需改动——
它只在 server target 下编译运行,sleep_ms 修复后其调用自然可编译。
|
2026-07-24 10:52:51 +08:00 |
|