Skip to content

feat: 轻量发文 posts 模块(编辑器直发 + /feed 原创 Tab + 个人主页 + 详情页 + 转正 PR)#350

Merged
longsizhuo merged 7 commits into
mainfrom
feat/posts-lightweight-publish
May 24, 2026
Merged

feat: 轻量发文 posts 模块(编辑器直发 + /feed 原创 Tab + 个人主页 + 详情页 + 转正 PR)#350
longsizhuo merged 7 commits into
mainfrom
feat/posts-lightweight-publish

Conversation

@longsizhuo
Copy link
Copy Markdown
Member

改动概述

新增「轻量发文」功能:用户在 /editor 写 Markdown 后直接 POST /api/posts 落后端 DB,无需 Git PR。

后端 PR:关联 feat/posts-module(Spring Boot posts 模块,含 posts 表、接口、SaToken 公开白名单)

前端改动清单

新增组件

  • app/types/post.ts — PostView / PostSummaryView / PostRequest 类型(与后端 DTO 对齐)
  • app/components/PostContent.tsx — UGC Markdown 渲染(react-markdown + rehype-sanitize,XSS 防护)
  • app/components/PromoteToDocsButton.tsx — 三态转正按钮(idle / pending / promoted,不可逆状态机)
  • app/[locale]/feed/components/FeedTabSwitcher.tsx — 原创文章 / 分享链接 Tab 切换
  • app/[locale]/feed/components/PostCard.tsx — 文章卡片(showAuthor prop,反色角标)
  • app/[locale]/u/[username]/PostsLinkOnProfile.tsx — 个人主页文章入口计数
  • app/[locale]/u/[username]/posts/ — 文章列表页(client)+ 详情页(SSR)+ layout

改动文件

  • EditorPageClient.tsx — 移除 DocsDestinationForm,改为 POST /api/posts 直发,成功后跳详情页
  • feed/page.tsx — 加 posts Tab(默认),fetchPosts() 三次退避策略
  • u/[username]/page.tsx — 追加 PostsLinkOnProfile 文章入口
  • next.config.mjs — 新增 /api/posts rewrite 规则(透传到后端)
  • robots.ts — disallow /u/*/posts/(UGC noindex)

Auth Header 约定

所有 /api/posts* fetch 用 satoken: token(rewrite 透传,与 /feed/submit 的 /api/community/links 一致)。
/api/upload 仍用 x-satoken(Next API Route,内部转换)。

Build 路由表

新增两条 ƒ Dynamic 路由(符合预期,无已有路由被意外翻成 ƒ):

  • ƒ /[locale]/u/[username]/posts — client 组件,读 localStorage 判 owner
  • ƒ /[locale]/u/[username]/posts/[slug] — SSR,cache: no-store

上线前提

后端 feat/posts-module 必须同步上线,且 SaTokenConfigure 公开读白名单包含:

  • GET /api/posts/feed
  • GET /api/posts/*/*

否则匿名用户访问 /feed 原创 Tab 和详情页会 401。

测试

tester 全轮验收通过(用真实 GitHub OAuth token 测发布主流程)。

开发者文档

dev_docs/posts_lightweight_publish.md

github-actions Bot added 2 commits May 24, 2026 15:49
新增 PostContent UGC Markdown 渲染器(react-markdown + rehype-sanitize,XSS 防护),
EditorPageClient 改造为直发 POST /api/posts,/feed 加原创文章默认 Tab,
/u/[username]/posts 列表页和 /u/[username]/posts/[slug] 详情页,
PromoteToDocsButton 三态按钮支持一键转正 PR,个人主页追加文章入口。
Copilot AI review requested due to automatic review settings May 24, 2026 15:51
@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
involutionhell-github-io Canceled Canceled May 24, 2026 4:38pm
website-preview Ready Ready Preview, Comment May 24, 2026 4:38pm

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “轻量发文” posts feature to the Next.js app so users can publish Markdown directly from /editor to the backend (no Git PR required), browse posts via a new /feed “原创文章” tab, and view user post lists/detail pages under /u/{identifier}/posts.

Changes:

  • Introduces posts typing + UI components (UGC Markdown renderer, post cards, profile entry link, promote-to-docs action).
  • Updates /editor publish flow to POST /api/posts and navigates to the new post detail route on success.
  • Extends routing/infra: /api/posts* rewrites, /feed top-level tab switcher, and robots rules to keep UGC out of indexing; adds developer docs.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new dependencies for runtime Markdown rendering/sanitization.
package.json Adds react-markdown + rehype-sanitize dependencies.
next.config.mjs Adds /api/posts and /api/posts/:path* rewrites to backend.
dev_docs/posts_lightweight_publish.md Documents posts lightweight publish flow, routes, and auth header conventions.
app/types/post.ts Adds frontend DTO typings aligned with backend posts module.
app/robots.ts Disallows indexing of /u/*/posts/ for UGC.
app/components/PromoteToDocsButton.tsx Adds promote-to-docs 3-state button and promote API call.
app/components/PostContent.tsx Adds UGC Markdown renderer with rehype pipeline + sanitization.
app/[locale]/u/[username]/PostsLinkOnProfile.tsx Adds profile entry link to posts list and (owner-only) count fetch.
app/[locale]/u/[username]/posts/page.tsx Adds client-rendered user posts list page with owner/public behavior.
app/[locale]/u/[username]/posts/layout.tsx Adds layout wrapper to keep Header/Footer server-rendered.
app/[locale]/u/[username]/posts/[slug]/PostDetailOwnerActions.tsx Adds owner-only actions (delete + promote).
app/[locale]/u/[username]/posts/[slug]/page.tsx Adds SSR post detail page with noindex metadata and PostContent rendering.
app/[locale]/u/[username]/page.tsx Wires profile page to show posts entry section.
app/[locale]/feed/page.tsx Adds “posts vs links” tab behavior and SSR fetching for posts feed.
app/[locale]/feed/components/PostCard.tsx Adds post summary card used by feed and user posts list.
app/[locale]/feed/components/FeedTabSwitcher.tsx Adds client tab switcher controlling `?tab=posts
app/[locale]/editor/EditorPageClient.tsx Switches editor publish from GitHub PR flow to direct posts API publish.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

attributes: {
...defaultSchema.attributes,
// 允许所有元素携带 className(rehype-katex / rehype-autolink-headings 需要)
"*": [...(defaultSchema.attributes?.["*"] ?? []), "className", "style"],
Comment on lines +4 to +7
import { DocsDestinationForm } from "@/app/components/DocsDestinationForm";
import { buildDocsNewUrl } from "@/lib/github";
import { buildFrontmatter } from "@/app/[locale]/editor/EditorPageClient";

Comment on lines +55 to +66
const token = localStorage.getItem("satoken") ?? "";
const res = await fetch(`/api/posts/${postId}`, {
method: "DELETE",
// rewrite 透传:后端读 satoken,不是 x-satoken
headers: { satoken: token },
});
const body = (await res.json()) as ApiResponse<void>;
if (res.ok && body.success) {
router.replace(`/u/${authorUsername}/posts`);
} else {
alert(body.message ?? `删除失败(HTTP ${res.status})`);
}
Comment on lines +25 to +28
/**
* 从文章标题生成 slug 候选值,和后端生成逻辑保持一致(kebab-case,纯 ASCII)。
* 后端会做唯一性去重,前端只是提前填充 filename input 用,不是最终 slug。
*/
Comment on lines +203 to +210
const postRequest: PostRequest = {
title: title.trim(),
description: description.trim() || undefined,
tags: tags.filter((t) => t.trim().length > 0),
contentMd: finalMarkdown,
// 有用户填的 slug 就带上,后端会去重;没有则不传,后端从 title 自动生成
...(rawSlug ? { slug: rawSlug } : {}),
};
Comment on lines +212 to +221
const res = await fetch("/api/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
// rewrite 透传:后端 sa-token.token-name=satoken,需用 satoken 而非 x-satoken
satoken: token,
},
body: JSON.stringify(postRequest),
signal: AbortSignal.timeout(30_000),
});
Comment on lines +112 to +121
const token = localStorage.getItem("satoken") ?? "";
fetch(`/api/posts/${postId}/promote`, {
method: "POST",
headers: {
"Content-Type": "application/json",
// rewrite 透传:后端读 satoken,不是 x-satoken
satoken: token,
},
body: JSON.stringify({ prUrl: githubUrl }),
}).catch((err) => {
github-actions Bot added 4 commits May 24, 2026 16:10
- 卡片 003「笔试面经」href: /docs/career/interview-prep/bq → /docs/career(死链修复)
- 卡片 004「群友分享」href: /docs/community → /feed?tab=posts(对接 posts 功能)
- Hero 右侧 CTA feed href: /feed → /feed?tab=posts
- zh CTA 文案「看看我们最近在读什么」→「看看群友在写什么」(与卡片标题「群友分享」区分)
- en CTA 文案 → "See what we're writing";community.desc 补全为 "Original posts from community members"
- zh community.desc「群友写的捏」→「群友原创文章,不定期更新」
- 003「笔试面经」href 恢复 /docs/career/interview-prep/bq(上次误改成 /docs/career 回滚)
- 004 标题「群友分享」→「群友创作」(与 feed 分享链接 Tab 区分)
- zh CTA「看看群友在写什么」→「去看群友在写什么 →」
- en CTA → "See what the community is writing →"
- en community.title → "Community Creations"
- 001/002/003 href 全部不动
- zh desc「群友原创文章,不定期更新」→「群友随手写的文章,不用等 PR review,发完即在。选题自由,从踩坑记录到技术思考都有。」
- en desc 同步更新(期待感文案,说明发完即在、选题自由)
- title/href/CTA 在上一 commit 已到位,本次仅补 desc
- title: "Community Creations" → "Community Posts"
- desc: 措辞对齐终版(published instantly, dev notes to technical deep-dives)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants