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( contentType: string, token: string, fields?: string, ): Promise { const query = new URLSearchParams({ content_type: contentType, token }); if (fields) { query.set("fields", fields); } try { return await fetchAPI(`/api/v2/page_preview/?${query.toString()}`, { revalidate: 0, }); } catch { return null; } } /** * 在页面组件中调用:若 Draft Mode 已开启且 cookie 中记录的 content_type * 与当前页面类型一致,返回草稿数据;否则回退到 `fallback()`(正常已发布内容)。 */ export async function getPreviewOrFallback( expectedContentType: string, fields: string, fallback: () => Promise, ): Promise { 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(expectedContentType, token, fields); return preview ?? fallback(); }