feat: 实现导航页基础功能
- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# Navius 后端
|
||||
|
||||
Rust + axum,数据用 JSON 文件存储,不依赖数据库。
|
||||
|
||||
网站分两种:**系统内置**(只读,随项目发布)和**提交/推送**(可改可删)。
|
||||
「不可修改」不是靠每处判断,而是靠**结构**保证:`system-*.json` 只在启动时读,
|
||||
代码里没有任何写这两个文件的路径。
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
cargo run # 默认监听 127.0.0.1:8080,数据目录 ./data
|
||||
cargo test # 34 个单元测试 + 接口测试
|
||||
cargo clippy --all-targets
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `NAVIUS_ADDR` | `127.0.0.1:8080` | 监听地址 |
|
||||
| `NAVIUS_DATA_DIR` | `data` | 数据目录 |
|
||||
| `NAVIUS_API_TOKEN` | 未设置 | 推送接口的令牌;不设置时 `/api/sites/push` 不可用(提交接口不受影响) |
|
||||
|
||||
## 数据文件
|
||||
|
||||
```
|
||||
data/
|
||||
├── site.json # 站点名与搜索框提示,只读
|
||||
├── system-categories.json # 内置分组,只读,自己手写维护
|
||||
├── system-sites.json # 内置网站,只读
|
||||
├── user-categories.json # 提交时创建的分组,自动写
|
||||
└── user-sites.json # 提交 / 推送的网站,自动写
|
||||
```
|
||||
|
||||
启动时会自检,任何一条不通过就直接退出(避免带着坏数据继续跑):
|
||||
|
||||
- `system-*.json` 里的 `source` 必须全是 `system`,`user-*.json` 里必须全不是;
|
||||
- 网站 id、分组 id 不能重复;
|
||||
- 每个网站的 `categoryId` 必须存在,且**内置网站只能挂在内置分组下、
|
||||
提交的网站只能挂在用户分组下**;
|
||||
- 分组的 `parentId` 必须存在、必须是一级分组(最多两级),也不能指向自己。
|
||||
|
||||
写盘用「先写临时文件再改名」,不会因为写到一半断电把文件写坏。
|
||||
|
||||
## 接口
|
||||
|
||||
### `GET /api/health`
|
||||
|
||||
```json
|
||||
{ "sites": 5, "systemSites": 5, "userSites": 0, "categories": 8 }
|
||||
```
|
||||
|
||||
### `GET /api/nav`
|
||||
|
||||
侧栏 + 内容区需要的那棵树。每个网站上都带一个推导出来的 `editable`
|
||||
(`source != "system"`),前端据此决定要不要显示编辑/删除入口。
|
||||
|
||||
```json
|
||||
{
|
||||
"site": { "name": "Suxing Nav", "searchHint": "输入关键字搜索全网资源..." },
|
||||
"groups": [
|
||||
{
|
||||
"id": "common", "title": "常用推荐", "source": "system", "editable": false,
|
||||
"sites": [
|
||||
{ "id": "github", "title": "GitHub", "url": "https://github.com",
|
||||
"description": "代码托管与开源社区", "categoryId": "common",
|
||||
"source": "system", "order": 10,
|
||||
"createdAt": "2026-09-13T10:29:48Z", "updatedAt": "2026-09-13T10:29:48Z",
|
||||
"submittedBy": "", "editable": false }
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`site` 来自 `data/site.json`:站点名和搜索框提示都由后端提供,前端不再写死。
|
||||
|
||||
### `POST /api/sites` — 用户提交
|
||||
|
||||
**不接受 `categoryId`**,只接受分组名;后端只在「用户创建的分组」里查找或新建,
|
||||
所以提交的内容不可能落进内置分组。
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Hugging Face",
|
||||
"url": "https://huggingface.co",
|
||||
"description": "开源模型与数据集社区",
|
||||
"group": "AI 工具",
|
||||
"subGroup": "模型社区",
|
||||
"submittedBy": "[email protected]"
|
||||
}
|
||||
```
|
||||
|
||||
成功返回 201 和创建出来的条目。同一父级下同名的用户分组会**复用**,
|
||||
不会每提交一次就多一个同名分组。
|
||||
|
||||
### `POST /api/sites/push` — 接口推送
|
||||
|
||||
入参同上,但需要请求头 `X-Api-Token: <NAVIUS_API_TOKEN>`,
|
||||
创建出来的条目 `source` 记为 `api`(id 用 `a-` 前缀)。
|
||||
|
||||
### `PATCH /api/sites/{id}` — 修改
|
||||
|
||||
只改传进来的字段,可传 `title` / `url` / `description` / `group` / `subGroup`。
|
||||
系统内置的条目返回 403;传 `group` 时同样只能在用户分组里找或建。
|
||||
|
||||
### `DELETE /api/sites/{id}` — 删除
|
||||
|
||||
系统内置的条目返回 403,删掉了返回 204。
|
||||
|
||||
### 错误响应
|
||||
|
||||
统一是 `{"error": "中文说明"}`:
|
||||
|
||||
| 状态码 | 场景 |
|
||||
| --- | --- |
|
||||
| 400 | 入参不合法(标题为空/超长、url 非法或指向内网、缺 group 等) |
|
||||
| 403 | 改删系统内置条目、推送令牌不对 |
|
||||
| 404 | 找不到条目 |
|
||||
| 500 | 写盘失败、推送接口未配置令牌 |
|
||||
|
||||
## 字段长度限制
|
||||
|
||||
标题 60 字、描述 120 字、分组名 30 字、`submittedBy` 120 字,都按**字符**算
|
||||
(中文一个字算一个)。集中在 `src/validate.rs` 顶部,改规则只改那一处。
|
||||
|
||||
## 模块
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.rs # 启动:读数据 → 自检 → 起 HTTP
|
||||
├── api.rs # 路由、令牌校验、把仓库错误翻译成状态码
|
||||
├── store.rs # 内存态仓库:增删改、构建导航树、原子写盘
|
||||
├── model.rs # 落盘结构、请求入参、对外返回结构
|
||||
├── validate.rs # 校验与归一化
|
||||
├── error.rs # ApiError(→ HTTP)与 StoreError(→ 启动失败)
|
||||
└── util.rs # 时间格式化与 id 生成
|
||||
```
|
||||
|
||||
## 已知取舍
|
||||
|
||||
- **跨域是全开的**(`CorsLayer::permissive`)。前端是 Flutter Web,开发时不同源;
|
||||
上线后如果只从自己的域名访问,应改成只允许那个来源。
|
||||
- **提交接口没有限流**。按需求「不需要审核、提交即上线」,公开写接口建议后面补
|
||||
一层限流或简单的验证码。
|
||||
- **`Cargo.lock` 目前在 `.gitignore` 里**。这是可执行程序,建议把它纳入版本管理,
|
||||
保证每台机器装到的依赖版本一致。
|
||||
- 前端目前还在用本地 mock 数据,接这个接口是下一步。
|
||||
Reference in New Issue
Block a user