Files
stash/README.md
T

256 lines
8.6 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、Tokio 和 Axum 实现的本地静态页面托管工具。它既可以直接托管一个静态站点,也可以扫描托管目录下的多个前端项目,并通过内置导航页统一访问。
## 核心功能
### 单站点托管
当托管根目录中存在 `index.html` 时,访问 `/` 会直接返回该页面。页面依赖的 JavaScript、CSS、图片、字体等资源继续通过静态文件服务加载,因此可以直接运行常见的纯静态前端项目。
### 多项目导航
当根目录没有 `index.html` 时,Stash 会扫描根目录下的一级子目录,并将包含 `index.html` 或 `index.htm` 的目录识别为前端项目。
- 自动排除以 `.` 开头的隐藏目录。
- 项目按名称进行不区分大小写的升序排列。
- 每个项目生成一个卡片链接,地址格式为 `/<项目目录名>/`。
- 项目名称首字符会作为圆形标记显示。
- 没有识别到项目时显示空状态提示。
- 导航页采用 4 列、2 列、1 列的响应式布局,分别适配桌面、平板和手机。
- 项目和目录列表在每次请求时重新扫描,新增项目通常无需重启服务。
- 文件监听会在项目快照发生变化后主动通知已打开的导航页,无需手动刷新。
### 静态文件服务
Stash 使用 `tower-http` 的 `ServeDir` 托管根目录内容:
- 自动为目录追加 `index.html`。
- 根据文件扩展名返回对应的 MIME 类型。
- 未找到的路径会返回内置的中文 404 页面,并显示请求路径。
- 每次 HTTP 请求都会经过 Trace 层,可通过调整日志级别观察请求日志。
- 内置控制台资源通过保留路径 `/__stash__/` 提供,避免与用户托管的项目文件冲突。
### 内置控制台
- 使用 Askama 在编译期渲染项目导航页和 404 页面。
- 页面 HTML、CSS 和 JavaScript 分别维护,不再使用字符串替换拼接卡片。
- 项目名称和请求路径由模板引擎自动进行 HTML 转义。
- 项目目录名会进行 URL 路径段编码,再生成项目访问地址。
- 少量 JavaScript 通过 SSE 订阅项目变化,并在不刷新整个页面的情况下更新卡片;脚本不可用时,服务端渲染的导航和手动 API 仍可正常使用。
### 同源 API
服务提供两个只读 JSON 接口和一个 SSE 推送接口,可供导航页或同源前端调用。
### CORS
默认允许任意来源、任意请求方法和任意请求头访问服务,适合本地联调。通过 `--no-cors` 可以关闭 CORS 标头注入。
### 服务管理
- 默认监听 `127.0.0.1:8080`。
- 支持自定义绑定地址、端口和托管目录。
- 默认托管目录 `./public` 不存在时会自动创建。
- 用户显式指定的托管目录不存在时,会输出错误并退出。
- 端口绑定失败、目录无效等错误会以清晰的错误信息返回。
- 支持分别通过 `Ctrl+C` 以及 Unix 系统的 `SIGTERM` 触发优雅停机。
### 可复用核心库
`stash-core` 同时作为库导出 `Config`、`StashError`、`start`、`ServerHandle`、`scan_projects` 和 `AppProject`,既可由 `stash-cli` 使用,也可以被其他 Rust 程序嵌入或复用。
## 快速开始
### 环境要求
- Rust 工具链,需支持 Rust 2024 Edition。
- Cargo。
### 启动示例
```bash
# 托管默认目录 ./public,监听 127.0.0.1:8080
cargo run -p stash-cli
# 托管指定目录并修改端口
cargo run -p stash-cli -- --root ./sites --port 3000
# 允许局域网设备访问
cargo run -p stash-cli -- --root ./sites --host 0.0.0.0 --port 3000
# 关闭 CORS
cargo run -p stash-cli -- --root ./sites --no-cors
```
启动成功后访问终端中显示的地址即可,例如:
```text
http://127.0.0.1:8080/
```
### 构建发布版本
```bash
cargo build --release -p stash-cli
```
构建产物为 `target/release/stash`;Windows 下为 `target/release/stash.exe`。
## 托管目录示例
### 单个静态站点
```text
public/
├── index.html
├── assets/
│ ├── app.js
│ └── style.css
└── images/
└── logo.png
```
此时访问 `/` 会直接展示 `public/index.html`。
### 多个前端项目
```text
public/
├── admin-console/
│ └── index.html
├── docs/
│ └── index.html
└── landing-page/
└── index.html
```
由于根目录没有 `index.html`,访问 `/` 时会展示项目导航页。对应访问地址为:
```text
/admin-console/
/docs/
/landing-page/
```
## 命令行参数
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `-r, --root <DIR>` | `./public` | 静态页面托管根目录,支持相对路径和绝对路径 |
| `-p, --port <PORT>` | `8080` | HTTP 服务监听端口 |
| `--host <HOST>` | `127.0.0.1` | 监听地址;使用 `0.0.0.0` 可供局域网访问 |
| `--no-listing` | 关闭 | 设计用于关闭根目录项目导航页 |
| `--no-cors` | 关闭 | 关闭全局 CORS 标头注入 |
| `-h, --help` | - | 查看命令行帮助 |
| `-V, --version` | - | 查看程序版本 |
注意:`--no-listing` 当前已由命令行解析并写入 `Config.directory_listing`,但根路径处理器尚未读取该配置。因此根目录没有 `index.html` 时,导航页目前仍会显示。
## 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/"
}
]
}
```
该接口与导航页使用相同的项目扫描规则。即使托管根目录包含 `index.html`,接口仍会返回当前根目录下可识别的项目列表。
### 订阅项目变化
```http
GET /api/projects/events
Accept: text/event-stream
```
连接建立后,服务会立即发送当前项目快照,随后在文件监听检测到项目列表变化时推送新快照:
```text
event: projects
data: {"projects":[{"name":"docs","route_path":"/docs/"}]}
```
服务端会递归监听托管目录,将短时间内的连续文件事件合并为一次扫描,并且只在项目名称或访问路径实际变化时广播。SSE 连接每 15 秒发送一次保活消息,浏览器断开后会自动重连。
## 项目结构
```text
.
├── Cargo.toml # Rust workspace 配置
├── Cargo.lock # 依赖锁定文件
├── README.md
├── stash-cli/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs # CLI 参数解析、启动信息和停机信号
└── stash-core/
├── Cargo.toml
├── assets/
│ ├── dashboard.css # 控制台与 404 页面样式
│ └── dashboard.js # 项目列表刷新增强
├── src/
│ ├── config.rs # 服务配置
│ ├── error.rs # 错误类型与 HTTP 错误响应
│ ├── lib.rs # 核心库导出
│ ├── listing.rs # 项目扫描与页面渲染
│ ├── server.rs # Axum 路由、SSE、静态托管与服务器生命周期
│ └── watcher.rs # 文件监听、事件防抖和项目快照广播
└── templates/
├── index.html # Askama 多项目导航页模板
└── 404.html # Askama 404 页面模板
```
## 测试
运行核心库测试:
```bash
cargo test -p stash-core
```
当前测试覆盖项目目录扫描、隐藏目录过滤、一级目录限制、项目排序、项目路径编码、空状态渲染、项目内容 HTML 转义、404 页面渲染与路径转义,以及新增项目后的文件监听广播。
## 已知限制
- 项目扫描只检查托管根目录的一级子目录,不递归查找更深层项目。
- 根目录存在 `index.html` 时会优先进入单站点模式,不再展示项目导航页。
- 静态服务只为目录自动追加 `index.html`;导航扫描虽然也识别 `index.htm`,但建议项目统一使用 `index.html`。
- 未配置 SPA History API 回退。刷新前端路由等不存在的物理路径时,会返回 404 页面。
- SSE 仅用于服务端向浏览器推送项目快照,不提供浏览器向服务端发送命令的双向通道。
- 文件事件会经过约 150ms 防抖后再扫描,因此项目出现在导航页中可能有短暂延迟。
- 启动横幅中显示的接口地址为 `/api/pages`,实际实现的项目列表接口是 `/api/projects`。
- 服务没有身份认证机制,默认仅建议绑定 `127.0.0.1`。使用 `0.0.0.0` 暴露到局域网前,应确认目录内容适合被其他设备访问。