feat: 可配置站点 URL 风格

- 新增 urlStyle 工具与测试:postPath/userPath/sitePagePath 统一生成链接
- 管理端新增 UrlStylePanel 设置面板,settings 服务与 handler 适配
- 全站路由链接与 JSON-LD/sitemap 切换为统一 URL 生成
- middleware 与相关组件适配
This commit is contained in:
2026-09-27 04:01:42 +08:00
parent 1d862a0dd3
commit 7a44c1f2cf
64 changed files with 1183 additions and 200 deletions

View File

@@ -3,6 +3,7 @@ import { ArrowLeft } from "lucide-react";
import type { PostListItem } from "@/lib/api";
import { formatRelative } from "@/lib/format";
import { getPublicSettingsCached } from "@/lib/serverData";
import { postPath, userPath } from "@/lib/urlStyle";
import { normalizePostType, postTypeLabel, typeStatusLabel, typeStatusClass } from "@/lib/postMeta";
import LikeButton from "./LikeButton";
import Avatar from "./Avatar";
@@ -32,7 +33,9 @@ export default async function PostRow({
showViews?: boolean;
showLike?: boolean;
}) {
const allowComments = (await getPublicSettingsCached()).allow_comments !== false;
const settings = await getPublicSettingsCached();
const allowComments = settings.allow_comments !== false;
const urlStyle = settings.url_style;
const lastReply = post.last_reply;
const replied =
allowComments &&
@@ -56,7 +59,7 @@ export default async function PostRow({
}}
>
<Link
href={`/u/${post.user.id}`}
href={userPath(urlStyle, post.user.id)}
className="shrink-0 above-link"
aria-label={authorName}
tabIndex={-1}
@@ -71,7 +74,7 @@ export default async function PostRow({
{post.recommended && <span className="badge-rec shrink-0">推荐</span>}
<div className="min-w-0 flex items-center gap-2 flex-1 overflow-hidden">
<Link
href={`/post/${post.id}`}
href={postPath(urlStyle, post.id)}
className="stretch-link min-w-0 shrink"
aria-label={post.title}
>
@@ -98,7 +101,7 @@ export default async function PostRow({
<span className="j13-board-tag">{post.board.name}</span>
)}
<Link
href={`/u/${post.user.id}`}
href={userPath(urlStyle, post.user.id)}
className="above-link font-medium hover:text-[var(--accent)] transition-colors"
>
{authorName}
@@ -109,7 +112,7 @@ export default async function PostRow({
<span className="inline-flex items-center gap-1 min-w-0">
<ArrowLeft size={13} className="shrink-0" aria-hidden />
<Link
href={`/u/${lastReply!.user.id}`}
href={userPath(urlStyle, lastReply!.user.id)}
className="above-link font-medium hover:text-[var(--accent)] transition-colors truncate max-w-[8em]"
>
{lastReply!.user.nickname || lastReply!.user.username}
@@ -128,7 +131,7 @@ export default async function PostRow({
<div className="flex items-center gap-3 sm:gap-4 shrink-0">
{allowComments ? (
<Link
href={`/post/${post.id}`}
href={postPath(urlStyle, post.id)}
tabIndex={-1}
aria-label={`${post.comment_count} 条评论`}
className={`${showViews ? "hidden sm:inline-flex" : "inline-flex"} above-link items-center gap-1.5 meta tabular-nums text-[13.5px] hover:text-[var(--accent)] transition-colors`}