Files
stash/README.md
T

171 lines
6.0 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
Stash 是一个用 Rust 编写的本地静态页面托管工具。编译后的 React 控制台会被内嵌进二进制,
所以发布物只有一个 `stash` 可执行文件,不需要额外部署前端静态资源。
它能做两件事:托管目录下的多个静态前端项目、给控制台提供同源 JSON / SSE 接口。
## 快速开始
```bash
# 1. 先构建前端(dist 会被内嵌,缺了它 cargo build 会直接失败)
cd stash-web
npm install
npm run build
cd ..
# 2. 编译并运行
cargo run # 托管 ./public,监听 127.0.0.1:8080
# 自定义目录 / 端口 / 对外暴露
cargo run -- --root ./sites --port 3000
cargo run -- --root ./sites --host 0.0.0.0 --port 3000
```
启动后打开终端里打印的地址即可,例如 `http://127.0.0.1:8080/`。
默认托管目录 `./public` 不存在时会自动创建。
环境要求:
- Rust 工具链(2024 Edition)
- Node.js 24,推荐用 [Volta](https://volta.sh/) 管理;`stash-web/package.json` 里已用 `volta` 字段固定版本
## 工作方式
请求按下面的顺序命中:
| 路径 | 处理方式 |
| --- | --- |
| `/api/*` | JSON / SSE 接口 |
| `/`、`/index.html` | 内嵌前端的 `index.html`(`no-cache`) |
| `/assets/**` | 内嵌前端的静态资源(文件名带 hash,长缓存) |
| `/favicon.svg` | 内嵌前端根目录文件 |
| 其他路径 | `ServeDir` 托管 `--root` 目录,目录自动补 `index.html` |
| 仍未命中 | 内置 404 页面(显示请求路径) |
托管目录下的每个一级子目录,只要包含 `index.html` 或 `index.htm`,就会被识别为一个项目,
通过 `/<目录名>/` 访问。目录名会做 URL 路径段编码。
## 目录结构
```text
.
├── Cargo.toml # 单 crate(lib + bin),依赖清单
├── Cargo.lock
├── build.rs # 把 stash-web/dist 同步到 $OUT_DIR/web
├── src/ # Rust 后端
│ ├── main.rs # CLI 参数、启动横幅、停机信号
│ ├── lib.rs # 库导出
│ ├── config.rs # 服务配置
│ ├── error.rs # 错误类型与 JSON 错误响应
│ ├── server.rs # 路由、内嵌静态资源、SSE、生命周期
│ ├── listing.rs # 托管目录扫描
│ └── watcher.rs # 文件监听、防抖、快照广播
├── stash-web/ # React + Vite + TypeScript 控制台
│ ├── package.json # 含 volta 版本固定
│ ├── vite.config.ts # 开发期 /api 代理到 127.0.0.1:8080
│ └── src/
└── public/ # 默认托管目录(可放多个前端项目)
```
`templates/` 与 `assets/` 是上一版 Askama 控制台留下的文件,当前已无任何代码引用,
可以直接删除。
## 前端怎么被内嵌
1. `npm run build` 产出 `stash-web/dist`;
2. `build.rs` 在编译 Rust 之前把 `dist` 复制到 `$OUT_DIR/web`(先清空,避免旧 hash 文件残留);
3. `src/server.rs` 用 `include_dir!("$OUT_DIR/web")` 在编译期把整棵目录树嵌进二进制;
4. 二进制运行时不再读取任何前端文件。
构建顺序不能颠倒:`dist` 不存在时 `build.rs` 会带着提示信息直接失败,而不是产出一个没有界面的二进制。
`build.rs` 声明了 `rerun-if-changed`,改动 `dist` 后重新 `cargo build` 会自动重新嵌入。
## 前端开发
```bash
cd stash-web
npm install
npm run dev # http://127.0.0.1:5173,/api 自动代理到后端 8080
npm run build # 产物输出到 stash-web/dist
npm run lint # oxlint
```
开发时前后端分开跑:一个终端 `cargo run`,另一个终端 `npm run dev`。
控制台的用户资料目前是占位数据(见 `stash-web/src/profile.ts`),后端尚未提供鉴权接口。
## 命令行参数
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `-r, --root <DIR>` | `./public` | 静态页面托管根目录 |
| `-p, --port <PORT>` | `8080` | 监听端口 |
| `--host <HOST>` | `127.0.0.1` | 监听地址,`0.0.0.0` 供局域网访问 |
| `--no-cors` | 关闭 | 关闭全局 CORS 标头注入 |
| `-h, --help` | - | 帮助 |
| `-V, --version` | - | 版本 |
`--no-listing` 参数目前仍能被解析,但由于控制台已改为内嵌前端,它不再影响任何行为。
## HTTP API
### 健康检查
```http
GET /api/health
```
```json
{ "status": "ok", "service": "stash" }
```
### 项目列表
```http
GET /api/projects
```
```json
{
"projects": [
{ "name": "admin-console", "route_path": "/admin-console/" },
{ "name": "docs", "route_path": "/docs/" }
]
}
```
### 订阅项目变化
```http
GET /api/projects/events
Accept: text/event-stream
```
连接建立后立即推送一次当前快照,之后在项目列表变化时推送:
```text
event: projects
data: {"projects":[{"name":"docs","route_path":"/docs/"}]}
```
服务端递归监听托管目录,把短时间内的连续事件合并成一次扫描,只在项目名称或访问路径真正变化时广播;
每 15 秒发送一次保活消息,浏览器断开后自动重连。
## 测试
```bash
cargo test # 目录扫描、隐藏目录过滤、排序、路径编码、文件监听广播
cd stash-web && npm run build # tsc -b + vite build,同时做类型检查
```
## 已知限制
- 控制台是单页应用但没有客户端路由,因此没有 SPA history 回退;刷新不存在的物理路径会返回 404。
- 内嵌资源占用了 `/assets/` 前缀,托管目录下如果存在同名目录会被遮蔽。
- `/` 固定由控制台占用,上一版「根目录有 `index.html` 就直接当单站点展示」的行为已取消;
托管目录里的项目统一走 `/<目录名>/`。
- 项目扫描只检查一级子目录,不递归查找更深层项目。
- 文件事件经过约 150ms 防抖,新项目出现在控制台可能有短暂延迟。
- 没有身份认证,默认只建议绑定 `127.0.0.1`;使用 `0.0.0.0` 暴露到局域网前请确认目录内容适合被其他设备访问。