593 lines
12 KiB
Markdown
593 lines
12 KiB
Markdown
---
|
||
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` / `en` / `ja` / `ko` / `vi` / `id` / `ms`;默认语言为 `en`。中文只有简体 `zh`,没有繁体中文。
|
||
- 错误格式:非 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" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
||
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=en
|
||
```
|
||
|
||
Response:
|
||
|
||
```ts
|
||
Category[]
|
||
```
|
||
|
||
用途:Home 资料分类、CategoryPage 标题、Admin 表单分类选择。
|
||
|
||
---
|
||
|
||
### 2.2 全部资料 / 分类资料流
|
||
|
||
```http
|
||
GET /api/posts?lang=en&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/en/ja/ko/vi/id/ms` |
|
||
|
||
Response:
|
||
|
||
```ts
|
||
PostListResponse;
|
||
```
|
||
|
||
排序:`publishedAt DESC`。
|
||
|
||
用途:
|
||
|
||
- `/browse`:不传 `category`
|
||
- `/category/:slug`:传 `category=<slug>`
|
||
|
||
---
|
||
|
||
### 2.3 Home 推荐资料
|
||
|
||
```http
|
||
GET /api/posts/recommended?lang=en&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=en&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=en&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" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
||
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
|