feat: 可配置站点 URL 风格
- 新增 urlStyle 工具与测试:postPath/userPath/sitePagePath 统一生成链接 - 管理端新增 UrlStylePanel 设置面板,settings 服务与 handler 适配 - 全站路由链接与 JSON-LD/sitemap 切换为统一 URL 生成 - middleware 与相关组件适配
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { absoluteCanonical, resolveSiteOrigin, type QueryBag } from "./siteUrl.ts";
|
||||
import { postPath, userPath, type UrlStyle } from "./urlStyle.ts";
|
||||
|
||||
/** 防止用户正文里的 </script> 提前闭合 JSON-LD 脚本 */
|
||||
export function serializeJsonLd(data: unknown): string {
|
||||
@@ -15,8 +16,8 @@ export type JsonLdPerson = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
function personNode(person: JsonLdPerson) {
|
||||
const url = person.id ? absoluteCanonical(`/u/${person.id}`) : undefined;
|
||||
function personNode(person: JsonLdPerson, urlStyle: UrlStyle = "default") {
|
||||
const url = person.id ? absoluteCanonical(userPath(urlStyle, person.id)) : undefined;
|
||||
return {
|
||||
"@type": "Person" as const,
|
||||
name: person.name,
|
||||
@@ -52,7 +53,9 @@ export function listingJsonLd(input: {
|
||||
items: { id: number; name: string }[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
urlStyle?: UrlStyle;
|
||||
}): Record<string, unknown> | null {
|
||||
const urlStyle = input.urlStyle ?? "default";
|
||||
const site = websiteNode(input.siteName, input.siteDescription);
|
||||
const pageUrl = absoluteCanonical(input.path, input.query);
|
||||
const origin = resolveSiteOrigin();
|
||||
@@ -75,7 +78,7 @@ export function listingJsonLd(input: {
|
||||
"@type": "ListItem",
|
||||
position: (input.page - 1) * input.pageSize + i + 1,
|
||||
name: item.name,
|
||||
url: `${origin}/post/${item.id}`,
|
||||
url: `${origin}${postPath(urlStyle, item.id)}`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
@@ -105,8 +108,10 @@ export function questionJsonLd(input: {
|
||||
pageUrl?: string;
|
||||
accepted?: JsonLdVisibleComment | null;
|
||||
answers: JsonLdVisibleComment[];
|
||||
urlStyle?: UrlStyle;
|
||||
}): Record<string, unknown> | null {
|
||||
const entityUrl = absoluteCanonical(`/post/${input.postId}`);
|
||||
const urlStyle = input.urlStyle ?? "default";
|
||||
const entityUrl = absoluteCanonical(postPath(urlStyle, input.postId));
|
||||
const origin = resolveSiteOrigin();
|
||||
const site = websiteId();
|
||||
if (!entityUrl || !origin || !site) return null;
|
||||
@@ -120,7 +125,7 @@ export function questionJsonLd(input: {
|
||||
text: a.content,
|
||||
dateCreated: a.created_at,
|
||||
url: commentUrl(entityUrl, a.id),
|
||||
...(a.author ? { author: personNode(a.author) } : {}),
|
||||
...(a.author ? { author: personNode(a.author, urlStyle) } : {}),
|
||||
});
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
@@ -137,7 +142,7 @@ export function questionJsonLd(input: {
|
||||
...(input.text ? { text: input.text } : {}),
|
||||
dateCreated: input.createdAt,
|
||||
...(input.updatedAt ? { dateModified: input.updatedAt } : {}),
|
||||
author: personNode(input.author),
|
||||
author: personNode(input.author, urlStyle),
|
||||
...(input.accepted && input.accepted.content.trim()
|
||||
? { acceptedAnswer: answerNode(input.accepted) }
|
||||
: {}),
|
||||
@@ -158,8 +163,10 @@ export function discussionJsonLd(input: {
|
||||
commentCount?: number;
|
||||
viewCount?: number;
|
||||
comments: JsonLdVisibleComment[];
|
||||
urlStyle?: UrlStyle;
|
||||
}): Record<string, unknown> | null {
|
||||
const entityUrl = absoluteCanonical(`/post/${input.postId}`);
|
||||
const urlStyle = input.urlStyle ?? "default";
|
||||
const entityUrl = absoluteCanonical(postPath(urlStyle, input.postId));
|
||||
const site = websiteId();
|
||||
if (!entityUrl || !site) return null;
|
||||
const pageUrl = input.pageUrl || entityUrl;
|
||||
@@ -174,7 +181,7 @@ export function discussionJsonLd(input: {
|
||||
datePublished: input.createdAt,
|
||||
...(input.updatedAt ? { dateModified: input.updatedAt } : {}),
|
||||
inLanguage: "zh-CN",
|
||||
author: personNode(input.author),
|
||||
author: personNode(input.author, urlStyle),
|
||||
isPartOf: { "@id": site },
|
||||
...(pageUrl !== entityUrl
|
||||
? {
|
||||
@@ -202,7 +209,7 @@ export function discussionJsonLd(input: {
|
||||
text: c.content,
|
||||
dateCreated: c.created_at,
|
||||
url: commentUrl(entityUrl, c.id),
|
||||
...(c.author ? { author: personNode(c.author) } : {}),
|
||||
...(c.author ? { author: personNode(c.author, urlStyle) } : {}),
|
||||
})),
|
||||
}
|
||||
: {}),
|
||||
@@ -215,8 +222,9 @@ export function profileJsonLd(input: {
|
||||
username: string;
|
||||
signature?: string;
|
||||
pageUrl?: string;
|
||||
urlStyle?: UrlStyle;
|
||||
}): Record<string, unknown> | null {
|
||||
const personUrl = absoluteCanonical(`/u/${input.id}`);
|
||||
const personUrl = absoluteCanonical(userPath(input.urlStyle ?? "default", input.id));
|
||||
if (!personUrl) return null;
|
||||
const pageUrl = input.pageUrl || personUrl;
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user