2026-05-25 05:25:57 +08:00
|
|
|
|
---
|
|
|
|
|
|
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`。
|
2026-05-26 10:03:12 +08:00
|
|
|
|
- 语言字段:`zh-CN` / `en` / `ja` / `ko` / `vi` / `id` / `ms`;默认语言为 `en`。中文只有简体 `zh-CN`,没有繁体中文。
|
2026-05-25 05:25:57 +08:00
|
|
|
|
- 错误格式:非 2xx + text/plain 或 JSON 均可;前端会显示错误文本。
|
|
|
|
|
|
- Admin 接口需要 `Authorization: Bearer <token>`。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 核心数据模型
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 Category
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
type Category = {
|
|
|
|
|
|
id: number;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
slug: string; // 用于 /category/:slug 和 GET /api/posts?category=<slug>
|
|
|
|
|
|
name: string; // 已按 lang 返回本地化名称
|
2026-05-25 05:25:57 +08:00
|
|
|
|
description?: string;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
iconKey: string; // folder/calendar/megaphone/video/image 等,前端已有 icon map
|
2026-05-25 05:25:57 +08:00
|
|
|
|
sortOrder: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 Post(新资料流核心)
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
type AttachmentKind = "image" | "video" | "document";
|
|
|
|
|
|
|
|
|
|
|
|
type Attachment = {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
kind: AttachmentKind;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
url: string; // 原始文件或可访问文件 URL
|
|
|
|
|
|
mime: string; // image/jpeg, application/pdf, video/mp4, ...
|
2026-05-25 05:25:57 +08:00
|
|
|
|
filename: string;
|
|
|
|
|
|
sizeBytes: number;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
width?: number; // image/video 建议提供
|
2026-05-25 05:25:57 +08:00
|
|
|
|
height?: number;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
durationSec?: number; // video 建议提供
|
|
|
|
|
|
posterUrl?: string; // video preview
|
|
|
|
|
|
thumbnailUrl?: string; // image/document preview
|
2026-05-25 05:25:57 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type Post = {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
categoryId: number;
|
|
|
|
|
|
categorySlug: string;
|
2026-05-26 10:03:12 +08:00
|
|
|
|
language: "zh-CN" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
2026-05-25 05:25:57 +08:00
|
|
|
|
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
|
2026-05-26 07:36:53 +08:00
|
|
|
|
GET /api/categories?lang=en
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
Category[]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
用途:Home 资料分类、CategoryPage 标题、Admin 表单分类选择。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 全部资料 / 分类资料流
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
2026-05-26 07:36:53 +08:00
|
|
|
|
GET /api/posts?lang=en&limit=20&cursor=<cursor>&type=all&language=&category=<slug>
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Query:
|
|
|
|
|
|
|
2026-05-26 07:36:53 +08:00
|
|
|
|
| 参数 | 必填 | 说明 |
|
|
|
|
|
|
| ---------- | ---: | ------------------------------------------- |
|
|
|
|
|
|
| `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` |
|
2026-05-25 05:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
PostListResponse;
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
排序:`publishedAt DESC`。
|
|
|
|
|
|
|
|
|
|
|
|
用途:
|
2026-05-26 07:36:53 +08:00
|
|
|
|
|
2026-05-25 05:25:57 +08:00
|
|
|
|
- `/browse`:不传 `category`
|
|
|
|
|
|
- `/category/:slug`:传 `category=<slug>`
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3 Home 推荐资料
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
2026-05-26 07:36:53 +08:00
|
|
|
|
GET /api/posts/recommended?lang=en&limit=12
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
{ items: Post[] }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
用途:Home「官方推荐」section。按 `sortOrder ASC` + `publishedAt DESC` 或后端自定义推荐顺序。
|
|
|
|
|
|
|
|
|
|
|
|
> 过渡期:当前前端 Home 仍可接受旧 `/api/resources/recommended`,但建议后端新做 `posts/recommended` 后前端再切换。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2.4 Home 最新资料
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
2026-05-26 07:36:53 +08:00
|
|
|
|
GET /api/posts/latest?lang=en&limit=8
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
{ items: Post[] }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
用途:Home「最新更新」section。按 `publishedAt DESC`。
|
|
|
|
|
|
|
|
|
|
|
|
> 过渡期:当前前端 Home 仍可接受旧 `/api/resources/latest`。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2.5 单条 Post(旧链接落地)
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/posts/:id
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
Post;
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
用途:旧 `/resource/:id` 前端重定向:拿 `categorySlug` 后跳到 `/category/<slug>#post-<id>`。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2.6 搜索
|
|
|
|
|
|
|
|
|
|
|
|
建议新接口:
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
2026-05-26 07:36:53 +08:00
|
|
|
|
GET /api/posts/search?q=<keyword>&lang=en&type=all&language=&cursor=&limit=20
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
PostListResponse;
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
搜索范围建议:`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` 参数建议按以下规则命中:
|
|
|
|
|
|
|
2026-05-26 07:36:53 +08:00
|
|
|
|
| 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?://` |
|
2026-05-25 05:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
## 4. Wallet Auth API
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 取得签名 nonce/message
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/auth/wallet/nonce
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{ "address": "0x..." }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
}
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 验证签名并签发 token
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/auth/wallet/verify
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
"address": "0x...",
|
|
|
|
|
|
"message": "...",
|
|
|
|
|
|
"signature": "0x..."
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
token: string;
|
|
|
|
|
|
}
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 验证当前 wallet session
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/auth/wallet/me
|
|
|
|
|
|
Authorization: Bearer <wallet-token>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
wallet: string;
|
|
|
|
|
|
}
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 5. Admin API
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 Admin 登录
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/admin/login
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{ "username": "...", "password": "..." }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
token: string;
|
|
|
|
|
|
}
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 Admin dashboard
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/admin/dashboard
|
|
|
|
|
|
Authorization: Bearer <admin-token>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
type AdminDashboard = {
|
2026-05-26 07:36:53 +08:00
|
|
|
|
totalResources: number; // 若迁移到 Post,可理解为 totalPosts
|
2026-05-25 05:25:57 +08:00
|
|
|
|
published: number;
|
|
|
|
|
|
todayNew: number;
|
|
|
|
|
|
totalViews: number;
|
|
|
|
|
|
totalDownloads: number;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
totalFavorites: number; // 收藏下线后可返回 0,避免旧 admin UI 崩
|
2026-05-25 05:25:57 +08:00
|
|
|
|
totalShares: number;
|
|
|
|
|
|
hotResources: {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
downloads: number;
|
2026-05-26 07:36:53 +08:00
|
|
|
|
favorites: number; // 可返回 0
|
2026-05-25 05:25:57 +08:00
|
|
|
|
views: number;
|
|
|
|
|
|
}[];
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 文件上传
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/admin/upload
|
|
|
|
|
|
Authorization: Bearer <admin-token>
|
|
|
|
|
|
Content-Type: multipart/form-data
|
|
|
|
|
|
|
|
|
|
|
|
file=<File>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
最低 Response:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
2026-05-26 07:36:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
url: string;
|
|
|
|
|
|
}
|
2026-05-25 05:25:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
建议 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;
|
2026-05-26 10:03:12 +08:00
|
|
|
|
language: "zh-CN" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
2026-05-25 05:25:57 +08:00
|
|
|
|
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
|