218 lines
12 KiB
Markdown
218 lines
12 KiB
Markdown
# Stash 交接文档
|
||
|
||
> 写于 2026-10-29。新会话第一件事:读这份 + `docs/ADMIN-PLAN.md` + `stash-ui/README.md`。
|
||
|
||
## 0. 一句话现状
|
||
|
||
Stash 已从「只读本地导航站」升级为**可登录、可配置、可编辑内容的本地发布台**:
|
||
|
||
- 登录(单用户,argon2id + Cookie 会话);
|
||
- `/admin` 设置页(页头滚动信息 / 首页卡片 / 归类 / 侧栏 / 资料 / 系统);
|
||
- 条目就地编辑(Drawer + 乐观锁);
|
||
- 分区新增/删除、**条目新增/删除(可选分区与分组)**、分组(子分类)新建/改名/删除、侧栏子分类配置、浏览/点赞/分享/跳转;
|
||
- 数据库自动迁移到 **v17**;
|
||
- 前端 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 | 能力 |
|
||
| --- | --- |
|
||
| 页头滚动信息 | 启用开关、速度 8–120、条目增删/排序、标签与色调、占位符 `{count}` `{project}` `{status}` |
|
||
| 首页卡片 | 显示/隐藏、标题覆盖、预览条数、顺序、**新增分类**、**删除分类**(不可删的显示灰色按钮) |
|
||
| 内容 | 弹窗新增应用/海报/图文条目(选分区 + 分组,分组可选「直属分区」);列表逐条编辑(含换分区/分组)与删除 |
|
||
| 分区 | 分区类型(应用/海报/图文)、标题、图标、色条、预览条数;首页卡片预览哪个分组;**任意类型的分组都可新建/改名/显隐/删除** |
|
||
| 侧栏导航 | 分类显示/隐藏、顺序、删除/新增;**任意类型**的子分组单独显隐与排序,并可直接新建子分组 |
|
||
| 资料 | 头像 URL、名称、角色、简介、位置(应用到侧栏资料卡) |
|
||
| 系统 | 修改密码(清空所有会话)、下载备份(VACUUM INTO 快照) |
|
||
|
||
保存:页头 / 首页卡片 / 资料走 `PATCH /api/settings`(`version` 乐观锁,冲突 409);「内容」「分区」逐项保存(条目、分区、分组各自接口);成功用 Toast(2.4s 自动消失)。
|
||
|
||
### 内容编辑
|
||
- 详情页 `[编辑]` 只在登录后显示;
|
||
- 打开右侧 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/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} 需登录
|
||
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 = 17`;`schema::migrate` 逐级升级:0 建全量、15→16→17、更高版本拒绝启动。
|
||
- 主要表:`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;上传需存储目录、大小/类型校验 |
|
||
| 拖拽排序 | 用上移/下移按钮替代 |
|
||
| 多用户/角色 | 未做,当前单管理员 |
|
||
| 分区编辑 UI | 已支持标题/图标/色条/类型/预览条数(`PATCH /api/categories/{id}`);改类型会自动迁移条目 |
|
||
| 条目移动 | 编辑弹窗里可直接换分区 / 分组(`PATCH /api/items/{key}` 的 `nodeKey`) |
|
||
| 分组管理 | 所有类型的分区都可建组/改名/显隐/删除(删除级联组内条目);分组拖拽排序未做 |
|
||
| 登录限流 | 进程内计数,重启清空 |
|
||
|
||
## 10. 约定与坑
|
||
|
||
- 回复用中文,表格化优先;视觉工作要截图验证。
|
||
- **任何共享组件改动**都要在 `docs/COMPONENTS.md` 补契约 + changelog,并说明兼容性。
|
||
- PowerShell 写文件注意 UTF-8;优先用编辑工具,别用 `Set-Content` 写带中文的源码。
|
||
- 改前端后必须 `npm run build` 再 `cargo build`,否则内嵌资源是旧的。
|
||
- 重启前先杀 `stash.exe`,否则 cargo 报「拒绝访问」。
|
||
- `/login` 是旧地址,`main.tsx` 会重定向到 `/`;登录已改为弹窗。
|
||
- 前端路由:`/`、`/c/<分区>`、`/c/<分区>/<分组>`、`/c/<分区>/i/<条目>`,`/admin` 独立壳。
|
||
|
||
## 11. 新会话建议的下一步
|
||
|
||
1. 先跑 `cargo test` + 打开 8080 冒烟,确认基线;
|
||
2. 若要继续产品化:条目移动 UI、回收站、图片上传、拖拽排序;
|
||
3. 若要发布:确认 `stash-ui` 以 `file:` 引入的方式在发布构建里如何锁定版本(建议改为 git tag 依赖或构建前 `npm pack`)。
|