82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
import { draftMode, cookies } from "next/headers";
|
||
import { fetchAPI } from "./api-client";
|
||
import type { WagtailPageSummary } from "@/types/wagtail";
|
||
|
||
/**
|
||
* Wagtail 预览模式(wagtail_headless_preview + Next.js Draft Mode)。
|
||
* 详见 documents/设计方案分析与完善版.md §2.16,后端实现见 apps/api/urls.py
|
||
* 的 PagePreviewAPIViewSet(挂载在 /api/v2/page_preview/)。
|
||
*/
|
||
|
||
export const PREVIEW_CONTENT_TYPE_COOKIE = "wagtail_preview_content_type";
|
||
export const PREVIEW_TOKEN_COOKIE = "wagtail_preview_token";
|
||
|
||
/** content_type(`app_label.model`,小写)→ 对应前端路由的解析函数 */
|
||
const PREVIEW_PATH_RESOLVERS: Record<
|
||
string,
|
||
(page: WagtailPageSummary) => string
|
||
> = {
|
||
"home.homepage": () => "/",
|
||
"blog.blogpage": (page) => `/blog/${page.meta.slug}`,
|
||
"products.productpage": (page) => `/products/${page.meta.slug}`,
|
||
"cases.casestudypage": (page) => `/cases/${page.meta.slug}`,
|
||
"solutions.solutionpage": (page) => `/solutions/${page.meta.slug}`,
|
||
};
|
||
|
||
/** 根据 content_type 解析预览内容应跳转到的前端路径,未知类型返回 null。 */
|
||
export function resolvePreviewPath(
|
||
contentType: string,
|
||
page: WagtailPageSummary,
|
||
): string | null {
|
||
const resolver = PREVIEW_PATH_RESOLVERS[contentType];
|
||
return resolver ? resolver(page) : null;
|
||
}
|
||
|
||
/**
|
||
* 拉取草稿页面数据。`fields` 省略时仅返回 Wagtail API 默认字段(含 meta.slug/meta.type),
|
||
* 用于 /preview 路由校验 token 是否有效。
|
||
*/
|
||
export async function fetchPreviewPage<T extends WagtailPageSummary>(
|
||
contentType: string,
|
||
token: string,
|
||
fields?: string,
|
||
): Promise<T | null> {
|
||
const query = new URLSearchParams({ content_type: contentType, token });
|
||
if (fields) {
|
||
query.set("fields", fields);
|
||
}
|
||
try {
|
||
return await fetchAPI<T>(`/api/v2/page_preview/?${query.toString()}`, {
|
||
revalidate: 0,
|
||
});
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 在页面组件中调用:若 Draft Mode 已开启且 cookie 中记录的 content_type
|
||
* 与当前页面类型一致,返回草稿数据;否则回退到 `fallback()`(正常已发布内容)。
|
||
*/
|
||
export async function getPreviewOrFallback<T extends WagtailPageSummary>(
|
||
expectedContentType: string,
|
||
fields: string,
|
||
fallback: () => Promise<T | null>,
|
||
): Promise<T | null> {
|
||
const { isEnabled } = await draftMode();
|
||
if (!isEnabled) {
|
||
return fallback();
|
||
}
|
||
|
||
const cookieStore = await cookies();
|
||
const contentType = cookieStore.get(PREVIEW_CONTENT_TYPE_COOKIE)?.value;
|
||
const token = cookieStore.get(PREVIEW_TOKEN_COOKIE)?.value;
|
||
|
||
if (contentType !== expectedContentType || !token) {
|
||
return fallback();
|
||
}
|
||
|
||
const preview = await fetchPreviewPage<T>(expectedContentType, token, fields);
|
||
return preview ?? fallback();
|
||
}
|