- 新 AppNext:首页按 /api/catalog2 的卡片勾选渲染(支持混合格式、每组条数),侧栏按分组树 + tree_hidden 渲染 - 分组页 /g/<key>、条目页 /g/<key>/i/<id>;条目在子树内递归查找,面包屑显示祖先链 - SPA 回落增加 /g/ 前缀;旧 /c/... 地址兼容 - 文档同步(路由、API、v19、限制) - 测试 59 passed;Playwright 验证首页/混合卡片/分组页/条目页/侧栏隐藏
12 KiB
12 KiB
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: 依赖引入。
启动 / 构建(顺序重要)
# 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,仅在需要对照时):
D:\CNWei\CNW\Rust\stash-old\target\debug\stash.exe --port 8081
测试
Windows 下 TEMP 权限有问题,跑测试要覆盖临时目录:
$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 | 能力 |
|---|---|
| 分组管理 | 多层分组树:新建根/子分组、改名、根分组格式(应用/海报/图文)、删除(级联子分组与条目) |
| 内容 | 选分组新增/编辑/删除条目;条目只属于一个分组 |
| 侧边栏 | 分组树逐项勾选(settings.sidebar.tree_hidden),子分组可单独勾选 |
| 首页卡片 | 新建/改名/删除卡片;勾选若干分组(可混合格式),每组单独设条数 |
| 页头 | 启用开关、速度、条目、标签与色调 |
| 资料 | 头像 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 = 19;schema::migrate逐级升级:0 建全量、15→16→17→18→19、更高版本拒绝启动。 node是统一分组树(根节点kind='section'带view_mode格式,子节点kind='group'可多层);card/card_group是首页卡片的展示组合(卡片不拥有内容)。- 主要表:
node、entry、entry_tag、section_block、admin、session、setting、audit。 entry.updated_at用于编辑乐观锁;setting表key='site'存站点 JSON。- 设置/内容都是 SQLite:备份用
/api/backup,恢复需停机替换文件。
6. 认证运维
- 忘记密码:删除
admin行(或整库)后重启 → 回到首次设置。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;上传需存储目录、大小/类型校验 |
| 拖拽排序 | 分组/卡片顺序暂用创建顺序;上移下移/拖拽未做 |
| 多用户/角色 | 未做,当前单管理员 |
| 分组移动 UI | 后端 PATCH /api/nodes/{key} 支持 parentKey 移动与防环;后台暂未暴露移动入口 |
| 条目移动 | 内容弹窗里可直接换分组(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. 新会话建议的下一步
- 先跑
cargo test+ 打开 8080 冒烟,确认基线; - 若要继续产品化:条目移动 UI、回收站、图片上传、拖拽排序;
- 若要发布:确认
stash-ui以file:引入的方式在发布构建里如何锁定版本(建议改为 git tag 依赖或构建前npm pack)。