Skip to content

Commit abab373

Browse files
docs: 补轻量发文 posts 模块开发者文档(数据流/约束/文件结构/上线清单)
1 parent 790a58b commit abab373

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# 轻量发文(Posts 模块)
2+
3+
用户可在 `/editor` 写 Markdown 文章,直接 `POST /api/posts` 落后端数据库,
4+
无需走 Git PR 流程。文章发布后可在 `/feed` 原创 Tab、个人主页 `/u/{username}/posts`
5+
和详情页 `/u/{username}/posts/{slug}` 查看,支持一键发起转正 PR 收录进 `/docs`
6+
7+
## 数据流
8+
9+
```
10+
浏览器(/editor)
11+
→ POST /api/posts (next.config.mjs rewrite 透传到后端)
12+
→ Spring Boot /api/posts (sa-token 鉴权,返回 PostView)
13+
→ 跳转 /u/{username}/posts/{slug}
14+
```
15+
16+
图片上传走独立路径:
17+
18+
```
19+
浏览器粘贴图片
20+
→ POST /api/upload (Next API Route,非 rewrite)
21+
→ 后端预签名 R2 URL
22+
→ 浏览器 PUT 直传 R2
23+
```
24+
25+
## 关键约束
26+
27+
**Auth Header**:所有 `/api/posts*` 的 fetch 用 `satoken: token`(rewrite 透传,
28+
后端 `sa-token.token-name=satoken` 直接读这个 header 名)。
29+
`/api/upload` 是 Next API Route,客户端用 `x-satoken`,由 route handler 内部翻译。
30+
31+
**BACKEND_URL**`fetchPosts()``fetchPost()` 在 Server Component 里直接调后端,
32+
必须配置 `BACKEND_URL` 环境变量。本地 dev 用 `http://localhost:8081`
33+
生产用 `https://api.involutionhell.com`。next.config.mjs 的 rewrite fallback 是
34+
`:8080`(生产镜像端口),但 posts 模块只在新后端部署,不依赖 fallback。
35+
36+
## 文件结构
37+
38+
| 文件 | 职责 |
39+
| -------------------------------------------------- | ----------------------------------------------------- |
40+
| `app/types/post.ts` | PostView / PostSummaryView / PostRequest 类型定义 |
41+
| `app/components/PostContent.tsx` | UGC Markdown 渲染(react-markdown + rehype-sanitize) |
42+
| `app/components/PromoteToDocsButton.tsx` | 三态转正按钮(idle / pending / promoted) |
43+
| `app/[locale]/editor/EditorPageClient.tsx` | 编辑器直发逻辑,`buildFrontmatter` 导出给转正按钮复用 |
44+
| `app/[locale]/feed/components/FeedTabSwitcher.tsx` | 原创文章 / 分享链接 Tab 切换(client 组件) |
45+
| `app/[locale]/feed/components/PostCard.tsx` | 文章卡片,`showAuthor` prop 控制作者显示 |
46+
| `app/[locale]/feed/page.tsx` | /feed 页,默认 Tab=posts,`fetchPosts()` 三次退避 |
47+
| `app/[locale]/u/[username]/posts/` | 个人文章列表页(client)+ 详情页(SSR) |
48+
| `app/[locale]/u/[username]/PostsLinkOnProfile.tsx` | 个人主页文章入口计数 |
49+
50+
## /feed Tab 行为
51+
52+
-`?tab``?tab=posts` → 原创文章(默认)
53+
- `?tab=links` → 分享链接(原有逻辑不变)
54+
- 切换 Tab 时:posts → links 保留 `?category`;links → posts 清空 `?category`
55+
56+
## 转正路径(PromoteToDocsButton)
57+
58+
1. idle → selecting:弹出 DocsDestinationForm 选目标目录
59+
2. selecting → pending:`window.open` 打开 GitHub 新建文件页(预填 frontmatter +
60+
正文),同时 fire-and-forget `POST /api/posts/{id}/promote`
61+
3. pending → promoted:后端写 `promotedAt`,用户刷新详情页后由 `initialPromoted=true`
62+
初始化进入 promoted 态
63+
64+
pending 态物理锁死(无 border/hover),不用 `disabled` 属性。
65+
66+
## 路由分类
67+
68+
| 路由 | 类型 | 原因 |
69+
| ------------------------------------- | --------- | -------------------------------------------- |
70+
| `/[locale]/u/[username]/posts` | ƒ Dynamic | 客户端组件,读 localStorage token 判定 owner |
71+
| `/[locale]/u/[username]/posts/[slug]` | ƒ Dynamic | SSR,`cache: "no-store"`,内容随时更新 |
72+
73+
两条新路由都是预期的 ƒ Dynamic,不影响已有路由分类。
74+
75+
## 上线检查清单
76+
77+
1. 后端 `feat/posts-module` 和前端 `feat/posts-lightweight-publish` 同步上线
78+
2. 后端 SaTokenConfigure 公开读白名单包含 `GET /api/posts/feed`
79+
`GET /api/posts/*/*`(否则匿名用户访问 /feed 和详情页会 401)
80+
3. 生产环境变量 `BACKEND_URL=https://api.involutionhell.com`(Vercel 配置)
81+
4. posts 表随 `SPRING_SQL_INIT_MODE=always` 首次启动自动建表

0 commit comments

Comments
 (0)