feat: 重构导航页并增加项目实时推送
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
# 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` 暴露到局域网前,应确认目录内容适合被其他设备访问。
|
||||
Reference in New Issue
Block a user