Files
stash/HANDOFF.md
T
CNWei f7d0630fe6 chore(settings): 去掉老库自动补内置引擎的兼容逻辑
- load() 不再把缺的内置引擎补进返回结果;老库缺内置引擎时保存会被 400 拒绝(不做自动迁移)
- 删除对应的 load_merges_new_builtin_engines 测试
- HANDOFF 同步说明
2026-11-06 13:51:28 +08:00

263 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Stash 交接文档
> 更新于 2026-11-04 19:30(Asia/Shanghai)。本文件是当前状态,取代旧版历史快照。
> 新会话先读这份,再按需看 `README.md`、`docs/ADMIN-PLAN.md`、`stash-ui/README.md`。
## 1. 一句话现状
Stash 是「本地项目托管 + 可配置导航首页」:Rust 单 crate 内嵌 React 前端,单管理员登录,内容库为 SQLite,分组 / 条目 / 首页卡片 / 站点设置都在 `/admin` 管理。
当前开发分支 `develop`,本地服务:
- 前台 + 后台:<http://127.0.0.1:8080>
- 管理后台:<http://127.0.0.1:8080/admin>
- 接口一览:<http://127.0.0.1:8080/interface>
- Vite 开发服务器 `5173` 当前**未运行**;需要时 `cd stash-web && npm run dev`
## 2. 仓库与分支
| 仓库 | 路径 | 分支 | 状态 |
| --- | --- | --- | --- |
| 主项目 | `D:\CNWei\CNW\Rust\stash` | `develop` | 工作区干净;功能最新 `1d1ece6`,本文件随后单独提交 |
| 组件库 | `D:\CNWei\CNW\Rust\stash-ui` | `master` | 工作区只有忽略的 `.idea/`;`994436a` 最新 |
- 主项目 remote 名为 `master`(不是 `origin`),地址 `https://gitea.ngmao.com/CNWei/stash.git`。
- `develop` 比 `master/develop` **领先 20 个提交,尚未推送**;此前 HTTPS 凭据不可用,需要时先准备 Gitea 凭据。
- `stash-ui` 也有 3 个未推送提交:`b97c77e`(SearchField suffix)、`46829cd`(Input leading)、`994436a`(EngineIcon)。
- `@stash/ui` 以 `file:../../stash-ui` 引入,`stash-web` 使用其 `dist/`;改组件库后必须重新 `npm run build`。
## 3. 启动 / 构建 / 测试
前端产物会被 `build.rs` 内嵌,顺序不能反:
```powershell
cd D:\CNWei\CNW\Rust\stash\stash-web
npm run build # tsc -b + vite,产物 dist/
cd D:\CNWei\CNW\Rust\stash
Get-Process stash -ErrorAction SilentlyContinue |
Where-Object { $_.Path -like '*stash\target*' } | Stop-Process -Force # 否则 exe 被占用
cargo build
Start-Process .\target\debug\stash.exe `
-RedirectStandardOutput "$env:TEMP\stash.log" `
-RedirectStandardError "$env:TEMP\stash-err.log" -WindowStyle Hidden
```
测试(Windows 下覆盖 TEMP 权限):
```powershell
$env:TEMP = "D:\CNWei\CNW\Rust\stash\target\tmp"; $env:TMP = $env:TEMP
cargo test # 交接时 52 passed
```
组件库:
```powershell
cd D:\CNWei\CNW\Rust\stash-ui
npm run build # dist/stash-ui.js + .css + types
```
## 4. 代码地图
```
src/
main.rs / lib.rs 启动、参数、日志
config.rs 端口 / 数据目录 / 托管目录
server.rs 全部 API + 内嵌前端 + SPA 回落
listing.rs 托管目录扫描
watcher.rs 文件监听 + SSE 防抖
db/
mod.rs Db 门面 + 迁移/种子测试
schema.rs 建表、种子、v15→…→v23 迁移
auth.rs admin / session / argon2id
settings.rs settings JSON + 校验
entries.rs 条目 CRUD、浏览、点赞、审计
groups.rs 分组树 CRUD / 移动 / 排序
cards.rs 卡片与勾选分组
catalog.rs 分组树 + 条目查询组装
stash-web/src/
AppNext.tsx 前台壳:路由、卡片、侧栏、搜索框、主题
main.tsx /admin 与普通前台分流
settings.ts SiteSettings 类型 / 默认值 / API 客户端
manage.ts 分组、卡片、catalog2 客户端
items.ts 条目编辑客户端
api.ts 项目扫描 / SSE 客户端
components/
admin/AdminNext.tsx 后台全部面板
header/SiteHeader.tsx 品牌 / 滚动信息 / 操作图标
content/ 卡片、网格、详情、编辑器
sidebar/ 资料卡、本地服务、导航
stash-ui/src/components/ SearchField / Input / EngineIcon 等
```
## 5. 数据库(v23)
路径:`target/debug/data/stash.db`(`--data` 可覆盖)。
- `PRAGMA user_version = 23`;支持 0 建全量、15→16→…→23 迁移,高于程序版本拒绝启动。
- 当前表:`admin`、`audit`、`card`、`card_group`、`entry`、`entry_tag`、`node`、`session`、`setting`。
- 当前数据(交接时):15 个 node、44 条 entry、5 张 card;`PRAGMA foreign_key_check` 无异常。
- `section_block` 与 `node.view_mode / empty_text / show_in_sidebar / allow_groups` 已在 v23 删除。
- `entry.format` 是每条内容自己的 `apps / posters / articles`,同一分组可混合。
- `node.kind = 'section'` 是根分组,`group` 是子分组;`enabled` 控制整棵子树显示。
- 条目只存 `node_key`;格式、分组、卡片都通过业务 key 关联。
- 备份:`target/debug/data/stash.db.bak-pre-cleanup-v22` 是 v23 改造前快照;`/api/backup` 可下载当前快照。
- **不要直接 `cargo clean`/删除整个 `target`**:运行库和备份在 `target/debug/data/`。
## 6. settings JSON
`setting` 表 `key='site'`,API `GET/PATCH /api/settings`,`version` 乐观锁。当前字段:
```jsonc
{
"header": { "enabled": true, "speed": 24, "gap": 26, "items": [...] },
"sidebar": { "show_profile": true, "show_service": false, "show_nav": true },
"background": { "light": "", "dark": "" }, // 空 = 内置背景
"font": { "source": "builtin", "current": "", "items": [] }, // builtin = 文楷;custom = 用 items 里 current 那条
"search": {
"default": "internal",
"engines": [
{ "id": "google", "name": "Google", "url": "https://www.google.com/search?q={query}", "icon": "", "enabled": true },
{ "id": "bing", "name": "Bing", "url": "https://www.bing.com/search?q={query}", "enabled": true },
{ "id": "baidu", "name": "百度", "url": "https://www.baidu.com/s?wd={query}", "enabled": true },
{ "id": "sogou", "name": "搜狗", "url": "https://www.sogou.com/web?query={query}", "enabled": true },
{ "id": "360", "name": "360", "url": "https://www.so.com/s?q={query}", "enabled": true },
{ "id": "duckduckgo", "name": "DuckDuckGo", "url": "https://duckduckgo.com/?q={query}", "enabled": true }
]
},
"profile": { "name": "...", "role": "...", "description": "...", "location": "...", "avatar": "" },
"site": { "title": "...", "favicon": "", "theme": "light", "brand": {...}, "actions": {...} }
}
```
- `search.default` 可以是 `internal` 或某个引擎 id;默认默认值是 `internal`。
- 上面是字段结构示例;实际值以 `GET /api/settings` 为准(交接时 `search.default=internal`、`site.theme=light`,background 两项使用自定义 picsum 图)。
- 外部引擎 URL 必须以 `http(s)://` 开头并包含 `{query}`,否则后端 400。
- 引擎 id 只能用小写字母 / 数字 / `-` / `_`(1–30 位,`internal` 保留);内置六个(含 DuckDuckGo)用品牌图标,
且**不能删除**(只能改名 / 改地址 / 停用)。不做老库兼容:库里的设置缺内置引擎时,保存会 400;
后台新加的引擎 id 自动生成为 `custom-xxx`、图标用通用地球(组件库 EngineIcon 自动回落)。
- 每个引擎可带 `icon`(png / svg 地址,`/` 开头的托管路径或 http(s) 链接):给了就用 `<img>` 渲染自己
的图标,留空回落到品牌图标 / 地球;SVG 走 img 是静态的,不执行脚本。
- `background.light/dark` 为空时用内置 `stash-web/src/assets/site-bg.jpg` / `site-bg-dark.jpg`;否则填托管目录 `public/` 路径或完整 URL。
- 旧 settings JSON 缺失字段会自动回落默认值;多余旧字段在下次保存时被丢弃。
## 7. 当前功能 / 后台 IA
后台左侧导航:
- **分组管理**:根分组 / 子分组树、编辑、移动、排序、启停、删除;
- **数据管理**:按主分组列出条目(含子分组),每条可选格式;
- **首页管理**
- 页头:开关、速度、滚动条目、品牌 / 图标 / 配色;
- 背景管理:亮色 / 暗色背景 URL + 预览 + 恢复内置;
- 搜索框:默认搜索项、外部引擎的名称 / URL / 启停、添加 / 删除自定义引擎、恢复默认;
- 侧边栏:资料卡 / 本地服务 / 分组导航显隐;
- 内容卡片:卡片 + 勾选分组 + 预览条数 + 排序;
- **资料管理**:头像、名称、角色、简介、位置;
- **系统管理**:改密码、下载备份、正文字体(先选内置 / 字体链接;链接模式下可存多套字体,启用其中一条)。
搜索框行为:
- 顶部悬浮标签显示当前引擎图标 + 名称,点击展开下拉;
- 第一项固定「站内搜索」,分隔线后是启用的外部引擎;
- 站内模式:输入过滤本地内容;回车打开第一个匹配条目/分组;
- 外部模式:输入**不再过滤**本地内容;回车用该引擎 URL 新标签页搜索;
- 当前选择存在本机 localStorage(`stash-search-engine`),刷新后仍用它;引擎被停用 / 删除时回落到后台默认项。
主题:
- 深色背景来自 Alright 演示站,资源 `site-bg-dark.jpg`;
- 明暗切换优先用 View Transition 整页快照交叉淡入(160ms),不支持时回退到 CSS 颜色过渡;
- 首帧由 `index.html` 内联脚本按 localStorage / 系统偏好先上色;
- 背景地址也缓存在 localStorage(`stash-background`),刷新首帧直接用它,设置回来再校准,不会先闪内置背景;
- 页头图标显示**当前**实际明暗(深色月亮 / 浅色太阳),点它是切到另一侧;
- 背景图两层(light/dark)交叉淡入。
## 8. HTTP API(当前保留)
```text
GET /api/health
GET /api/projects GET /api/projects/events (SSE)
GET /api/catalog2 分组树 + 卡片
GET /api/catalog/events SSE:catalog 变化
GET /api/settings
PATCH /api/settings 需登录,{version,settings}
GET /api/fonts font/ 目录里的字体清单(公开读)
POST /api/nodes 需登录
PATCH /api/nodes/{key} 需登录
DELETE /api/nodes/{key} 需登录
POST /api/cards 需登录
PATCH /api/cards/{id} 需登录
DELETE /api/cards/{id} 需登录
PUT /api/cards/{id}/groups 需登录
GET /api/items/{key}
POST /api/items 需登录
PATCH /api/items/{key} 需登录
DELETE /api/items/{key} 需登录
POST /api/items/{key}/view
POST /api/items/{key}/like
GET /api/backup 需登录
GET /api/auth/session
POST /api/auth/setup / login / logout
PATCH /api/auth/password
```
已删除:`/api/catalog`、`/api/categories/*`、旧 `/api/groups/{id}`。
## 9. 组件库改动(stash-ui)
- `SearchField` 新增可选 `suffix` 与 `leading` 插槽;
- `Input` 新增 `leading` ReactNode,优先于 `icon`;
- 新增 `EngineIcon`(`internal/google/bing/baidu/sogou/360`),图标路径来自参考稿;`18px` 默认、`14px` 用于顶部小标签;
- 构建产物已更新,`stash-web` 通过 file 依赖读取;改库后必须重建 dist,否则主项目构建仍用旧类型/资源。
## 10. 已知限制 / 坑
| 项 | 说明 |
| --- | --- |
| 推送 | 本地 `develop` 领先远端 20 个提交,尚未推送;Gitea HTTPS 凭据需准备 |
| 搜索引擎 | 内置六个(含 DuckDuckGo,有品牌图标,不能删只能停用)+ 后台添加的自定义引擎(最多 10 个,通用地球图标) |
| 搜索选择记忆 | 存本机 localStorage(`stash-search-engine`);换设备 / 清站点数据后回到后台默认 |
| 图片上传 | 背景 / 头像 / Logo 只支持 URL,不支持上传 |
| 卡片/分组拖拽 | 排序用数值 / 现有 SortableList;分组拖拽排序未做 |
| 备份恢复 | 只有 `/api/backup` 下载;恢复需停机替换库文件 |
| 旧接口 | 已删;不要再按旧文档调用 `/api/catalog` 或 categories |
| 双份示例 | `public/git_helper/` 是 Rust 托管示例;`stash-web/public/git_helper/` 是 Vite dev 示例,**两份都保留** |
| exe 占用 | 重启/构建前先杀 stash.exe,否则 `cargo build` 拒绝访问 |
| 编码 | 源码 UTF-8;PowerShell 控制台可能显示乱码,以 read 工具为准,别用 Set-Content 改中文源码 |
## 11. 最近提交(主项目,倒序)
```text
1d1ece6 fix(group): 分组面包屑保留详情页外框
9d1e5dc fix(group): 分组页面包屑与详情页统一
4862f8f fix(search): 外部引擎模式不再过滤本地内容
aa6f341 feat(search): 按参考稿重做搜索框并支持后台配置
5ae2088 style(search): 引擎图标恢复 18px
200908c style(search): 引擎图标统一使用组件库图标
56f362f feat(search): 点击放大镜切换搜索引擎
8c1c658 feat(search): 效果五快捷栏加入本地搜索
038b7ec feat(search): 搜索框支持切换搜索引擎
a5d7859 fix(theme): 用整页快照过渡消除明暗割裂
5439ebf feat(admin): 增加背景管理
b9005dd style(theme): 暗色使用 Alright 同款深色背景
```
组件库最近:
```text
994436a feat(icons): 增加搜索引擎图标
46829cd feat(input): 增加左侧 leading 插槽
b97c77e feat(search): SearchField 增加 suffix 插槽
```
## 12. 新会话建议顺序
1. 打开 <http://127.0.0.1:8080> 与 `/admin` 冒烟;确认 8080 在跑(当前 PID 会变,不必依赖 PID)。
2. 改前端:`cd stash-web && npm run build` → 杀 stash → `cargo build` → 启动。
3. 改组件库:先 `cd stash-ui && npm run build`,再构建 `stash-web`,最后 Rust。
4. 改设置结构:同时改 `src/db/settings.rs`、`stash-web/src/settings.ts`、后台面板和默认值;旧 JSON 由 serde 默认值兜底。
5. 改数据库结构:升 `SCHEMA_VERSION` 并补迁移与迁移测试;先备份 `target/debug/data/stash.db`。
6. 视觉改动用 Playwright 截图到 `stash-ui/shots/` 验证;脚本在 `stash-ui/.ref/`(不提交)。
7. 收尾跑 `cargo test`(52 条)和 `npm run build`,确认工作区干净。