# Stash 交接文档 > 写于 2026-10-29。新会话第一件事:读这份 + `docs/ADMIN-PLAN.md` + `stash-ui/README.md`。 ## 0. 一句话现状 Stash 已从「只读本地导航站」升级为**可登录、可配置、可编辑内容的本地发布台**: - 登录(单用户,argon2id + Cookie 会话); - `/admin` 设置页(页头滚动信息 / 首页卡片 / 归类 / 侧栏 / 资料 / 系统); - 条目就地编辑(Drawer + 乐观锁); - 分区新增/删除、**条目新增/删除(可选分区与分组)**、分组(子分类)新建/改名/删除、侧栏子分类配置、浏览/点赞/分享/跳转; - 数据库自动迁移到 **v18**; - 前端 100% 使用组件库 `@stash/ui`(v0.3.1,42 个组件)。 当前数据库**已设置管理员密码**(2026-10-30 由使用者设置);忘记密码按 §6 删除 `admin` 行即可回到首次设置。 ## 1. 仓库与运行环境 | 项目 | 路径 | 分支 | 端口 | 说明 | | --- | --- | --- | --- | --- | | 新版(组件重构) | `D:\CNWei\CNW\Rust\stash` | `feat/use-stash-ui` | **8080** | 所有新功能都在这里 | | 旧版(1:1 基线) | `D:\CNWei\CNW\Rust\stash-old`(worktree) | `develop` | **8081** | 只用于视觉对照,不要改 | | 组件库 | `D:\CNWei\CNW\Rust\stash-ui` | `master` | — | tag `v0.1.0` / `v0.2.0` / `v0.3.0` / `v0.3.1` | 后端:Rust 2024 + Axum 0.8 + rusqlite(bundled)。前端:React 19 + Vite + TS + CSS Modules,`@stash/ui` 通过 `file:` 依赖引入。 ### 启动 / 构建(顺序重要) ```powershell # 1) 前端(产物会被 build.rs 内嵌,必须先构建) cd D:\CNWei\CNW\Rust\stash\stash-web npm run build # 2) 先杀掉正在运行的 stash.exe,否则 cargo 会报「拒绝访问」 Get-Process stash -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*stash\target*' } | Stop-Process -Force # 3) 后端 cd .. cargo build Start-Process .\target\debug\stash.exe -RedirectStandardOutput "$env:TEMP\stash.log" -RedirectStandardError "$env:TEMP\stash-err.log" -WindowStyle Hidden ``` 旧版(8081,仅在需要对照时): ```powershell D:\CNWei\CNW\Rust\stash-old\target\debug\stash.exe --port 8081 ``` ### 测试 Windows 下 `TEMP` 权限有问题,跑测试要覆盖临时目录: ```powershell $env:TEMP = "D:\CNWei\CNW\Rust\stash\target\tmp"; $env:TMP = $env:TEMP cargo test # 当前 48 passed ``` ## 2. 目录 / 文件地图 ``` src/ main.rs / lib.rs 启动、日志 config.rs 端口 / 数据目录(默认 target/debug/data) server.rs 全部路由(API + 内嵌前端 + SPA 回落) error.rs StashError(含 Conflict → 409) db/ mod.rs Db 门面(所有方法)+ 迁移测试 schema.rs 建表、种子、版本迁移(0/15→16→17) auth.rs 管理员、密码哈希、会话(token 只存 sha256) settings.rs settings JSON(header/sidebar/sections/profile) entries.rs 条目读取/编辑、浏览数、点赞、审计 categories.rs 分区新增/删除 catalog.rs 查询组装(前端三种视图) stash-web/src/ App.tsx 主页壳、登录弹窗、编辑器、设置应用 main.tsx 入口分流(/admin 与 /login) auth.ts / settings.ts / items.ts 前端 API 客户端 components/ header/SiteHeader.tsx 品牌 + 滚动信息 + 操作图标 admin/AdminPage.tsx /admin 全部 Tab auth/LoginDialog.tsx Modal 登录 auth/PasswordInput.tsx 带显隐切换的密码框 content/ 首页、分区、条目、文章、编辑器 sidebar/ 资料卡、服务面板、导航 docs/ADMIN-PLAN.md 后台方案 + P0–P4 实施记录 + 未做项 README.md 运行、路由、库结构、登录说明 ``` ## 3. 功能清单(已完成) ### 登录 - 单用户 `admin`,密码 argon2id; - 会话:随机 32 字节 token,HttpOnly + SameSite=Strict + Path=/,7 天,库里存 sha256; - 登录失败 5 次锁 60 秒;写接口校验 Origin;写接口需要会话。 ### `/admin`(新原子模型) | Tab | 能力 | | --- | --- | | 分组管理 | 树形表格:新建根分组(左侧)、加子分组、编辑(名称/上级/格式/图标/排序/状态)、删除;行内状态开关即时生效;图标可留空 | | 内容 | 选分组以列表展示条目(选主分组时包含所有子分组);列:标题 / 跳转链接 / 所属分组 / 操作;弹窗新增/编辑,编辑时显示不可改的「标识」 | | 侧边栏 | 分组树逐项勾选,开关在右侧、**拨动即保存**(无单独保存/撤销按钮);子分组可单独勾选 | | 首页卡片 | 新建/改名/删除卡片;勾选若干分组(可混合格式),每组单独设条数 | | 页头 | 启用开关、速度、条目、标签与色调 | | 资料 | 头像 URL、名称、角色、简介、位置 | | 系统 | 修改密码、下载备份 | 保存:分组/内容/卡片逐项保存;侧边栏、页头、资料走 `PATCH /api/settings`(`version` 乐观锁)。 ### 内容编辑 - 详情页 `[编辑]` 只在登录后显示; - 打开右侧 Drawer:标题、简介、标签、一句话评价、正文 Markdown、封面、跳转地址; - `PATCH /api/items/{key}`,`updatedAt` 乐观锁; - 保存后 `catalog.reload()` 局部刷新,不整页刷新。 ### 详情操作条 - 浏览:进入页面 `POST /api/items/{key}/view`,数字 +1; - 点赞:可切换,`POST /like`,localStorage 记住状态; - 分享:复制当前链接,Toast 反馈; - 跳转:保存时规范化 `href`(`www.` 自动补 `https://`,拒绝 `javascript:` 等协议);为空或 `#` 时按钮置灰。 ## 4. HTTP API ``` GET /api/health GET /api/projects GET /api/projects/events (SSE) GET /api/catalog GET /api/catalog/events (SSE) 旧结构(兼容) GET /api/catalog2 分组树 + 卡片(新前台/后台) GET /api/cards 卡片列表 POST /api/nodes {key,title,parentKey?,format?} 需登录 PATCH /api/nodes/{key} {title,parentKey?,sort?,format?} 需登录 DELETE /api/nodes/{key} 需登录,级联 POST /api/cards {title} 需登录 PATCH /api/cards/{id} {title?,sort?} 需登录 DELETE /api/cards/{id} 需登录 PUT /api/cards/{id}/groups [{groupKey,previewLimit}] 需登录 GET /api/auth/session -> {authenticated, has_admin, username} POST /api/auth/setup {password} 仅当无管理员 POST /api/auth/login {password} POST /api/auth/logout PATCH /api/auth/password {current, next} GET /api/settings -> {version, settings} PATCH /api/settings {version, settings} 需登录 GET /api/items/{key} -> 原始字段(编辑表单) POST /api/items {key,nodeKey,item} 需登录 PATCH /api/items/{key} {updatedAt, item, nodeKey?} 需登录,nodeKey 可移动条目 DELETE /api/items/{key} 需登录 POST /api/items/{key}/view POST /api/items/{key}/like {liked} POST /api/categories {id,title,kind} 需登录 PATCH /api/categories/{id} {title,icon,barColor,kind,previewLimit,emptyText,blockNodeKey,allowGroups} 需登录 POST /api/categories/{id}/groups {id,title} 需登录,仅应用分区 DELETE /api/categories/{id} 需登录,级联,local 不可删 PATCH /api/groups/{id} {title,showInSidebar} 需登录 DELETE /api/groups/{id} 需登录,级联组内条目 GET /api/backup SQLite 快照附件 需登录 ``` ## 5. 数据库与迁移 - 文件:开发态在 `target/debug/data/stash.db`;可用 `--root` / 数据目录覆盖(见 `config.rs`)。 - 当前 `PRAGMA user_version = 20`;`schema::migrate` 逐级升级:0 建全量、15→16→17→18→19→20、更高版本拒绝启动。 - `node` 是统一分组树(根节点 `kind='section'` 带 `view_mode` 格式,子节点 `kind='group'` 可多层);`enabled` 控制启停;`icon` 可为空。 - 主要表:`node`、`entry`、`entry_tag`、`section_block`、`admin`、`session`、`setting`、`audit`。 - `entry.updated_at` 用于编辑乐观锁;`setting` 表 `key='site'` 存站点 JSON。 - **设置/内容都是 SQLite**:备份用 `/api/backup`,恢复需停机替换文件。 ## 6. 认证运维 - 忘记密码:删除 `admin` 行(或整库)后重启 → 回到首次设置。 ```powershell node -e "const {DatabaseSync}=require('node:sqlite');const d=new DatabaseSync('target/debug/data/stash.db');d.exec('DELETE FROM admin; DELETE FROM session;')" ``` - 改密码:`/admin → 系统`,成功后该账号所有会话失效,需要重新登录。 - 首个管理员由 `/api/auth/setup` 创建,密码至少 8 位。 ## 7. 组件库 `@stash/ui` - 42 个组件,文档站:`cd stash-ui && npm run dev`(也可 `npm run build:site` 后看 `dist-site`)。 - 关键命令:`npm run typecheck && npm run lint && npm run build && npm run build:site`。 - 组件契约:`docs/COMPONENTS.md`;设计规范:`DESIGN.md`;外部风格接入流程:`docs/STYLE-INTAKE.md`(闸门:截图 → 映射 → 决策 → 评审 → 再写代码)。 - 主题:`themes/example.css` 只改变量;页面禁止写组件内部选择器。 - **判断归属**:全局配置放 `/admin`;页面版式(间距、布局容器)写在页面 CSS;只有「第二个页面也复用」的才抽组件。 ## 8. 验证与截图 - 后端逻辑:`cargo test`(48 passed,覆盖迁移/登录/设置/条目编辑/分区)。 - 前端构建:`stash-web npm run build`(tsc -b + vite)。 - 视觉/交互:Playwright 脚本在 `stash-ui/.ref/*.mjs`(不提交 git),截图在 `stash-ui/shots/`。 - `measure-final.mjs` **已过时**(旧 1:1 对照用),选择器 `.app header` 仍可用; - `c3-check.mjs`、`d-check.mjs`、`p2-check.mjs` 是后期功能检查脚本。 - 常用冒烟:首页卡片、`/admin` 各 Tab、详情编辑、登录弹窗。 ## 9. 已知限制 / 未做 | 项 | 状态 | | --- | --- | | 备份恢复/导入 | 只提供下载快照;恢复需停机替换 `stash.db` | | 回收站/软删除 | 未做,删除分组会级联删子分组与条目(有二次确认) | | 图片上传 | 只支持 URL;上传需存储目录、大小/类型校验 | | 拖拽排序 | 分组/卡片顺序暂用创建顺序;上移下移/拖拽未做 | | 多用户/角色 | 未做,当前单管理员 | | 分组移动 | 编辑弹窗里可改「上级分组」(防环);根分组不能移到子级 | | 条目移动 | 内容弹窗里可直接换分组(`PATCH /api/items/{key}` 的 `nodeKey`) | | 分组排序 | 可编辑「排序权重」数值;拖拽排序未做 | | 旧接口 | `/api/catalog`、`/api/categories/*`、`/api/groups/*` 仍保留给旧组件/兼容,后续可删 | | 登录限流 | 进程内计数,重启清空 | ## 10. 约定与坑 - 回复用中文,表格化优先;视觉工作要截图验证。 - **任何共享组件改动**都要在 `docs/COMPONENTS.md` 补契约 + changelog,并说明兼容性。 - PowerShell 写文件注意 UTF-8;优先用编辑工具,别用 `Set-Content` 写带中文的源码。 - 改前端后必须 `npm run build` 再 `cargo build`,否则内嵌资源是旧的。 - 重启前先杀 `stash.exe`,否则 cargo 报「拒绝访问」。 - `/login` 是旧地址,`main.tsx` 会重定向到 `/`;登录已改为弹窗。 - 前端路由:`/`、`/g/<分组 key>`、`/g/<分组 key>/i/<条目>`;旧 `/c/...` 地址按第一个路径段当分组 key 兼容,`/admin` 独立壳。 ## 11. 新会话建议的下一步 1. 先跑 `cargo test` + 打开 8080 冒烟,确认基线; 2. 若要继续产品化:条目移动 UI、回收站、图片上传、拖拽排序; 3. 若要发布:确认 `stash-ui` 以 `file:` 引入的方式在发布构建里如何锁定版本(建议改为 git tag 依赖或构建前 `npm pack`)。