feat: add telegram-style resource stream
This commit is contained in:
@@ -0,0 +1,581 @@
|
||||
---
|
||||
title: "ARK Library Frontend — Backend API Requirements"
|
||||
type: api-requirements
|
||||
date: 2026-05-25
|
||||
audience: backend
|
||||
status: draft
|
||||
---
|
||||
|
||||
# ARK Library Frontend — Backend API Requirements
|
||||
|
||||
这份文档列出前端接下来需要后端提供的**全部接口**。重点是新的 Telegram-style 资料流;旧 `resources` 接口可作为过渡,但最终建议统一到 `posts` 模型。
|
||||
|
||||
## 0. 通用约定
|
||||
|
||||
- API base:前端通过 `VITE_API_URL` 指向后端;本地可同源 `/api`。
|
||||
- 上传文件可通过 `/uploads/...` 或完整 URL 返回;前端会用 `assetUrl()` 处理相对路径。
|
||||
- 所有时间字段使用 ISO 8601 字符串,例如 `2026-05-24T14:42:00.000Z`。
|
||||
- 语言字段:`zh-TW` / `zh-CN` / `en`。
|
||||
- 错误格式:非 2xx + text/plain 或 JSON 均可;前端会显示错误文本。
|
||||
- Admin 接口需要 `Authorization: Bearer <token>`。
|
||||
|
||||
## 1. 核心数据模型
|
||||
|
||||
### 1.1 Category
|
||||
|
||||
```ts
|
||||
type Category = {
|
||||
id: number;
|
||||
slug: string; // 用于 /category/:slug 和 GET /api/posts?category=<slug>
|
||||
name: string; // 已按 lang 返回本地化名称
|
||||
description?: string;
|
||||
iconKey: string; // folder/calendar/megaphone/video/image 等,前端已有 icon map
|
||||
sortOrder: number;
|
||||
};
|
||||
```
|
||||
|
||||
### 1.2 Post(新资料流核心)
|
||||
|
||||
```ts
|
||||
type AttachmentKind = "image" | "video" | "document";
|
||||
|
||||
type Attachment = {
|
||||
id: string;
|
||||
kind: AttachmentKind;
|
||||
url: string; // 原始文件或可访问文件 URL
|
||||
mime: string; // image/jpeg, application/pdf, video/mp4, ...
|
||||
filename: string;
|
||||
sizeBytes: number;
|
||||
width?: number; // image/video 建议提供
|
||||
height?: number;
|
||||
durationSec?: number; // video 建议提供
|
||||
posterUrl?: string; // video preview
|
||||
thumbnailUrl?: string; // image/document preview
|
||||
};
|
||||
|
||||
type Post = {
|
||||
id: string;
|
||||
categoryId: number;
|
||||
categorySlug: string;
|
||||
language: "zh-TW" | "zh-CN" | "en";
|
||||
text?: string;
|
||||
attachments: Attachment[];
|
||||
isRecommended: boolean;
|
||||
publishedAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type PostListResponse = {
|
||||
items: Post[];
|
||||
nextCursor?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 1.3 Post 显示规则(后端必须按这个模型返回)
|
||||
|
||||
- 纯文字/链接:`attachments: []`,`text` 非空。
|
||||
- 单张图片:`attachments.length === 1` 且 `kind: "image"`。
|
||||
- 图片 + 文字:`kind: "image"` + `text`。
|
||||
- 视频:`kind: "video"`,建议提供 `posterUrl` / `durationSec`。
|
||||
- 文件:`kind: "document"`,前端显示下载卡。
|
||||
- 图片当文件上传:`kind: "document"` 且 `mime` 是 image;前端会显示缩略图 + 下载按钮。
|
||||
- 多图:
|
||||
- 2 / 3 / 4 张图:前端会独立纵向显示每张图,同一个 Post 只显示一次时间。
|
||||
- 超过 4 张图:前端显示 2×2 合并格,第 4 格模糊并显示 `+N`。
|
||||
|
||||
## 2. Public API(前台用户)
|
||||
|
||||
### 2.1 分类列表
|
||||
|
||||
```http
|
||||
GET /api/categories?lang=zh-CN
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
Category[]
|
||||
```
|
||||
|
||||
用途:Home 资料分类、CategoryPage 标题、Admin 表单分类选择。
|
||||
|
||||
---
|
||||
|
||||
### 2.2 全部资料 / 分类资料流
|
||||
|
||||
```http
|
||||
GET /api/posts?lang=zh-CN&limit=20&cursor=<cursor>&type=all&language=&category=<slug>
|
||||
```
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|---|---:|---|
|
||||
| `lang` | 是 | UI 语言 |
|
||||
| `limit` | 否 | 默认 20,最大建议 50 |
|
||||
| `cursor` | 否 | 后端返回的不透明 cursor |
|
||||
| `category` | 否 | 不传 = 全部资料;传 slug = 单分类 |
|
||||
| `type` | 否 | `all/image/video/ppt/pdf/text/link/archive` |
|
||||
| `language` | 否 | 资料语言:`zh-TW/zh-CN/en` |
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
PostListResponse
|
||||
```
|
||||
|
||||
排序:`publishedAt DESC`。
|
||||
|
||||
用途:
|
||||
- `/browse`:不传 `category`
|
||||
- `/category/:slug`:传 `category=<slug>`
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Home 推荐资料
|
||||
|
||||
```http
|
||||
GET /api/posts/recommended?lang=zh-CN&limit=12
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ items: Post[] }
|
||||
```
|
||||
|
||||
用途:Home「官方推荐」section。按 `sortOrder ASC` + `publishedAt DESC` 或后端自定义推荐顺序。
|
||||
|
||||
> 过渡期:当前前端 Home 仍可接受旧 `/api/resources/recommended`,但建议后端新做 `posts/recommended` 后前端再切换。
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Home 最新资料
|
||||
|
||||
```http
|
||||
GET /api/posts/latest?lang=zh-CN&limit=8
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ items: Post[] }
|
||||
```
|
||||
|
||||
用途:Home「最新更新」section。按 `publishedAt DESC`。
|
||||
|
||||
> 过渡期:当前前端 Home 仍可接受旧 `/api/resources/latest`。
|
||||
|
||||
---
|
||||
|
||||
### 2.5 单条 Post(旧链接落地)
|
||||
|
||||
```http
|
||||
GET /api/posts/:id
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
Post
|
||||
```
|
||||
|
||||
用途:旧 `/resource/:id` 前端重定向:拿 `categorySlug` 后跳到 `/category/<slug>#post-<id>`。
|
||||
|
||||
---
|
||||
|
||||
### 2.6 搜索
|
||||
|
||||
建议新接口:
|
||||
|
||||
```http
|
||||
GET /api/posts/search?q=<keyword>&lang=zh-CN&type=all&language=&cursor=&limit=20
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
PostListResponse
|
||||
```
|
||||
|
||||
搜索范围建议:`text`、`filename`、`categoryName`、tags。
|
||||
|
||||
过渡期当前前端仍使用:
|
||||
|
||||
```http
|
||||
GET /api/resources?q=<keyword>&lang=&type=&language=&limit=50
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ items: Resource[] }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.7 搜索日志
|
||||
|
||||
```http
|
||||
POST /api/search-log
|
||||
Content-Type: application/json
|
||||
|
||||
{ "query": "ARK" }
|
||||
```
|
||||
|
||||
Response:204 或 `{ ok: true }`。
|
||||
|
||||
用途:记录用户搜索词;失败不阻断用户体验。
|
||||
|
||||
---
|
||||
|
||||
### 2.8 下载统计(可选)
|
||||
|
||||
文件下载目前前端可直接打开 `Attachment.url`。如果后端需要统计下载,提供:
|
||||
|
||||
```http
|
||||
POST /api/posts/:postId/attachments/:attachmentId/download
|
||||
```
|
||||
|
||||
Response:204 或 `{ ok: true }`。
|
||||
|
||||
> 过渡期旧 Home 推荐卡还可能调用 `POST /api/resources/:id/download`。
|
||||
|
||||
## 3. Filter 语义
|
||||
|
||||
`GET /api/posts` 的 `type` 参数建议按以下规则命中:
|
||||
|
||||
| type | 命中条件 |
|
||||
|---|---|
|
||||
| `all` | 全部 |
|
||||
| `image` | 任一 attachment `kind === "image"` |
|
||||
| `video` | 任一 attachment `kind === "video"` 或 `mime.startsWith("video/")` |
|
||||
| `pdf` | 任一 attachment 扩展名 `pdf` 或 `mime === "application/pdf"` |
|
||||
| `ppt` | 任一 attachment 扩展名 `ppt/pptx/key` 或 mime 含 `presentation` |
|
||||
| `archive` | 任一 attachment 扩展名 `zip/rar/7z/tar/gz` |
|
||||
| `text` | `text` 非空 |
|
||||
| `link` | `text` 中包含 `https?://` |
|
||||
|
||||
## 4. Wallet Auth API
|
||||
|
||||
### 4.1 取得签名 nonce/message
|
||||
|
||||
```http
|
||||
POST /api/auth/wallet/nonce
|
||||
Content-Type: application/json
|
||||
|
||||
{ "address": "0x..." }
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ message: string }
|
||||
```
|
||||
|
||||
### 4.2 验证签名并签发 token
|
||||
|
||||
```http
|
||||
POST /api/auth/wallet/verify
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"address": "0x...",
|
||||
"message": "...",
|
||||
"signature": "0x..."
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ token: string }
|
||||
```
|
||||
|
||||
### 4.3 验证当前 wallet session
|
||||
|
||||
```http
|
||||
GET /api/auth/wallet/me
|
||||
Authorization: Bearer <wallet-token>
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ wallet: string }
|
||||
```
|
||||
|
||||
## 5. Admin API
|
||||
|
||||
### 5.1 Admin 登录
|
||||
|
||||
```http
|
||||
POST /api/admin/login
|
||||
Content-Type: application/json
|
||||
|
||||
{ "username": "...", "password": "..." }
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ token: string }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Admin dashboard
|
||||
|
||||
```http
|
||||
GET /api/admin/dashboard
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
type AdminDashboard = {
|
||||
totalResources: number; // 若迁移到 Post,可理解为 totalPosts
|
||||
published: number;
|
||||
todayNew: number;
|
||||
totalViews: number;
|
||||
totalDownloads: number;
|
||||
totalFavorites: number; // 收藏下线后可返回 0,避免旧 admin UI 崩
|
||||
totalShares: number;
|
||||
hotResources: {
|
||||
id: string;
|
||||
title: string;
|
||||
downloads: number;
|
||||
favorites: number; // 可返回 0
|
||||
views: number;
|
||||
}[];
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3 文件上传
|
||||
|
||||
```http
|
||||
POST /api/admin/upload
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file=<File>
|
||||
```
|
||||
|
||||
最低 Response:
|
||||
|
||||
```ts
|
||||
{ url: string }
|
||||
```
|
||||
|
||||
建议 Response(更方便前端自动建 Attachment):
|
||||
|
||||
```ts
|
||||
{
|
||||
url: string;
|
||||
mime: string;
|
||||
filename: string;
|
||||
sizeBytes: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
durationSec?: number;
|
||||
thumbnailUrl?: string;
|
||||
posterUrl?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Admin Post 列表
|
||||
|
||||
```http
|
||||
GET /api/admin/posts?limit=25&page=1&status=&category=&type=&q=
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{
|
||||
items: AdminPost[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
type AdminPost = Post & {
|
||||
isPublic: boolean;
|
||||
sortOrder: number;
|
||||
status: "draft" | "published" | "archived";
|
||||
viewCount?: number;
|
||||
downloadCount?: number;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.5 Admin Post 详情
|
||||
|
||||
```http
|
||||
GET /api/admin/posts/:id
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
Response:`AdminPost`。
|
||||
|
||||
---
|
||||
|
||||
### 5.6 创建 Post
|
||||
|
||||
```http
|
||||
POST /api/admin/posts
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
Request:
|
||||
|
||||
```ts
|
||||
type UpsertPostPayload = {
|
||||
categoryId: number;
|
||||
language: "zh-TW" | "zh-CN" | "en";
|
||||
text?: string;
|
||||
attachments: Attachment[];
|
||||
isPublic: boolean;
|
||||
isRecommended: boolean;
|
||||
sortOrder: number;
|
||||
status: "draft" | "published" | "archived";
|
||||
publishedAt?: string;
|
||||
tags?: string[];
|
||||
};
|
||||
```
|
||||
|
||||
Response:`AdminPost`。
|
||||
|
||||
---
|
||||
|
||||
### 5.7 更新 Post
|
||||
|
||||
```http
|
||||
PUT /api/admin/posts/:id
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
Request:`UpsertPostPayload`。
|
||||
Response:`AdminPost`。
|
||||
|
||||
---
|
||||
|
||||
### 5.8 删除 Post
|
||||
|
||||
```http
|
||||
DELETE /api/admin/posts/:id
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
Response:204 或 `{ ok: true }`。
|
||||
|
||||
---
|
||||
|
||||
### 5.9 Admin 搜索日志
|
||||
|
||||
```http
|
||||
GET /api/admin/search-logs?limit=300
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{
|
||||
items: {
|
||||
id: string;
|
||||
query: string;
|
||||
createdAt: string;
|
||||
count?: number;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 过渡期旧 Resource API(如果 admin 尚未迁移)
|
||||
|
||||
当前部分前端/admin 代码仍可能使用旧接口。后端可以短期保留,或前端后续再统一切到 Post:
|
||||
|
||||
```http
|
||||
GET /api/resources?lang=&limit=&page=&sort=&q=&type=&language=&tag=
|
||||
GET /api/resources/recommended?lang=&limit=
|
||||
GET /api/resources/latest?lang=&limit=
|
||||
POST /api/resources/:id/download
|
||||
GET /api/admin/resources?limit=&page=
|
||||
GET /api/admin/resources/:id
|
||||
POST /api/admin/resources
|
||||
PUT /api/admin/resources/:id
|
||||
```
|
||||
|
||||
旧 `Resource` shape:
|
||||
|
||||
```ts
|
||||
type Resource = {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
type: string;
|
||||
language: string;
|
||||
categoryId: number;
|
||||
categorySlug: string;
|
||||
categoryName: string;
|
||||
coverImage?: string;
|
||||
fileUrl?: string;
|
||||
previewUrl?: string;
|
||||
externalUrl?: string;
|
||||
bodyText?: string;
|
||||
badgeLabel?: string;
|
||||
isDownloadable: boolean;
|
||||
isRecommended: boolean;
|
||||
publishedAt?: string;
|
||||
updatedAt: string;
|
||||
tags?: string[];
|
||||
};
|
||||
```
|
||||
|
||||
## 7. 已下线 / 不需要实现
|
||||
|
||||
- 用户收藏:`/favorites` 页面已移除。
|
||||
- `POST /api/resources/:id/favorite` 不需要。
|
||||
- Reaction / 点赞 / 评论不需要。
|
||||
- Telegram 管理员标签、头像、群组名不需要。
|
||||
|
||||
## 8. 前后端切换计划
|
||||
|
||||
1. 后端先实现 `/api/posts`、`/api/posts/:id`、`/api/categories`。
|
||||
2. 前端 staging 设置:`VITE_USE_MOCK_POSTS=false`。
|
||||
3. 确认 `/browse` 和 `/category/:slug` 正常拉真数据。
|
||||
4. 再实现 `/api/posts/recommended`、`/api/posts/latest`,前端把 Home 从旧 resources 切到 posts。
|
||||
5. 最后迁移 Admin 从 `/api/admin/resources` 到 `/api/admin/posts`。
|
||||
|
||||
## 9. 最小可上线优先级
|
||||
|
||||
### P0(前台资料流必需)
|
||||
|
||||
- `GET /api/categories`
|
||||
- `GET /api/posts`
|
||||
- `GET /api/posts/:id`
|
||||
- `POST /api/admin/upload`
|
||||
- `POST /api/admin/posts`
|
||||
- `PUT /api/admin/posts/:id`
|
||||
- `GET /api/admin/posts`
|
||||
- `GET /api/admin/posts/:id`
|
||||
|
||||
### P1(首页/搜索/统计)
|
||||
|
||||
- `GET /api/posts/recommended`
|
||||
- `GET /api/posts/latest`
|
||||
- `GET /api/posts/search`
|
||||
- `POST /api/search-log`
|
||||
- `GET /api/admin/dashboard`
|
||||
- `GET /api/admin/search-logs`
|
||||
|
||||
### P2(账户)
|
||||
|
||||
- Wallet nonce / verify / me
|
||||
Reference in New Issue
Block a user