From 64a9e27ca72832efe9ead389923672b13868acd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 15 Jan 2024 09:42:38 +0100 Subject: [PATCH] Resolve revision and change request URL correctly (#98) * Add tests for getURLLookupAlternatives * Match correctly revisions and changes URLs * Use revision and changerequets ID passed from lookup * Prevent indexing these urls * Start * Start rendering toolbar * Update api client * Format * Format * Mock resolve of snippet * Add test for revision --- bun.lockb | Bin 448752 -> 448752 bytes package.json | 2 +- src/app/[spaceId]/(content)/layout.tsx | 5 + src/app/[spaceId]/(core)/sitemap.xml/route.ts | 4 +- src/app/[spaceId]/fetch.ts | 35 ++++--- src/components/AdminToolbar/AdminToolbar.tsx | 95 ++++++++++++++++++ src/components/AdminToolbar/index.ts | 1 + src/components/Search/SearchScopeToggle.tsx | 4 +- .../TableOfContents/ToggleableLinkItem.tsx | 2 +- src/lib/api.ts | 37 ++++++- src/lib/cache/cache.ts | 5 +- src/lib/middleware.test.ts | 67 ++++++++++++ src/lib/middleware.ts | 87 ++++++++++++++++ src/lib/references.ts | 8 ++ src/lib/seo.ts | 9 +- src/middleware.ts | 81 +++------------ tests/visual-testing.ts | 10 +- 17 files changed, 362 insertions(+), 90 deletions(-) create mode 100644 src/components/AdminToolbar/AdminToolbar.tsx create mode 100644 src/components/AdminToolbar/index.ts create mode 100644 src/lib/middleware.test.ts create mode 100644 src/lib/middleware.ts diff --git a/bun.lockb b/bun.lockb index 946566f15333174c31cfebcdeb629040f59eda08..1fa2f708631acd1fdf9503c37c8a07e5eb641f47 100755 GIT binary patch delta 166 zcmV;X09pU=u^aHQ8;~v_Dt10fksW@k`c+4@>Zfs9AB&9qe$Wpi=eNwTh_T22u}=1j z0UDEmQ7e;hNC<;?jJJ4<0lj}fEirYZ6hmhsm)7 Ux5=>uh9&_vhfvrBmr&RSpo6zcWdHyG delta 166 zcmV;X09pU=u^aHQ8;~v_{7z#~&x|7!bu8strzvFl1f!Cs)?BI@LDzIAmpal5u}=1j z0U48lQ7e;hNC<;?jJJ4<0lj}fmG@Fyxmi185R8kc>y@~2~;EitwD9~hjk1Bhjk1Cw{;8zQ>6hlhsm)7 Ux5=>uh9&_uhfvrBmr&RSplCr%BLDyZ diff --git a/package.json b/package.json index 19a31fb98..23d714902 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@geist-ui/icons": "^1.0.2", - "@gitbook/api": "^0.25.0", + "@gitbook/api": "^0.26.0", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-popover": "^1.0.7", "@readme/openapi-parser": "^2.5.0", diff --git a/src/app/[spaceId]/(content)/layout.tsx b/src/app/[spaceId]/(content)/layout.tsx index 3b9d848d3..17630f225 100644 --- a/src/app/[spaceId]/(content)/layout.tsx +++ b/src/app/[spaceId]/(content)/layout.tsx @@ -3,6 +3,7 @@ import { Metadata, Viewport } from 'next'; import Script from 'next/script'; import React from 'react'; +import { AdminToolbar } from '@/components/AdminToolbar'; import { CookiesToast } from '@/components/Cookies'; import { SpaceLayout } from '@/components/SpaceLayout'; import { getContentSecurityPolicyNonce } from '@/lib/csp'; @@ -57,6 +58,10 @@ export default async function ContentLayout(props: { ) : null} + + {content.revisionId || content.changeRequestId ? ( + + ) : null} ); } diff --git a/src/app/[spaceId]/(core)/sitemap.xml/route.ts b/src/app/[spaceId]/(core)/sitemap.xml/route.ts index 0729688c7..93e006db6 100644 --- a/src/app/[spaceId]/(core)/sitemap.xml/route.ts +++ b/src/app/[spaceId]/(core)/sitemap.xml/route.ts @@ -5,7 +5,7 @@ import { NextRequest } from 'next/server'; import { getRevisionPages } from '@/lib/api'; import { pageHref } from '@/lib/links'; -import { SpaceParams } from '../../fetch'; +import { SpaceParams, getContentPointer } from '../../fetch'; export const runtime = 'edge'; @@ -13,7 +13,7 @@ export const runtime = 'edge'; * Generate a sitemap.xml for the current space. */ export async function GET(req: NextRequest, { params }: { params: SpaceParams }) { - const rootPages = await getRevisionPages(params); + const rootPages = await getRevisionPages(getContentPointer(params)); const pages = flattenPages(rootPages); const urls = pages.map(({ page, depth }) => { // Decay priority with depth diff --git a/src/app/[spaceId]/fetch.ts b/src/app/[spaceId]/fetch.ts index 87eb9a8ef..04b7004eb 100644 --- a/src/app/[spaceId]/fetch.ts +++ b/src/app/[spaceId]/fetch.ts @@ -1,4 +1,5 @@ import { ContentVisibility, RevisionPage, Space } from '@gitbook/api'; +import { headers } from 'next/headers'; import { getCollectionSpaces, @@ -10,7 +11,9 @@ import { } from '@/lib/api'; import { resolvePagePath, resolvePageId } from '@/lib/pages'; -export type SpaceParams = ContentPointer; +export interface SpaceParams { + spaceId: string; +} export interface PagePathParams extends SpaceParams { pathname?: string[]; @@ -20,16 +23,25 @@ export interface PageIdParams extends SpaceParams { pageId?: string; } +/** + * Get the current content pointer from the params. + */ +export function getContentPointer(params: PagePathParams | PageIdParams) { + const headerSet = headers(); + const content: ContentPointer = { + spaceId: params.spaceId, + revisionId: headerSet.get('x-gitbook-content-revision') ?? undefined, + changeRequestId: headerSet.get('x-gitbook-content-changerequest') ?? undefined, + }; + + return content; +} + /** * Fetch all the data needed to render the space layout. */ export async function fetchSpaceData(params: PagePathParams | PageIdParams) { - const content: ContentPointer = { - spaceId: params.spaceId, - changeRequestId: params.changeRequestId, - revisionId: params.revisionId, - }; - + const content = getContentPointer(params); const { space, pages, customization, scripts } = await getSpaceContent(content); const collection = await fetchParentCollection(space); @@ -49,12 +61,7 @@ export async function fetchSpaceData(params: PagePathParams | PageIdParams) { * Optimized to fetch in parallel as much as possible. */ export async function fetchPageData(params: PagePathParams | PageIdParams) { - const content: ContentPointer = { - spaceId: params.spaceId, - changeRequestId: params.changeRequestId, - revisionId: params.revisionId, - }; - + const content = getContentPointer(params); const { space, pages, customization, scripts } = await getSpaceContent(content); const page = await resolvePage(pages, content, params); @@ -120,5 +127,5 @@ async function fetchParentCollection(space: Space) { */ export function getPathnameParam(params: PagePathParams): string { const { pathname } = params; - return pathname ? pathname.map(part => decodeURIComponent(part)).join('/') : ''; + return pathname ? pathname.map((part) => decodeURIComponent(part)).join('/') : ''; } diff --git a/src/components/AdminToolbar/AdminToolbar.tsx b/src/components/AdminToolbar/AdminToolbar.tsx new file mode 100644 index 000000000..d1b63bc96 --- /dev/null +++ b/src/components/AdminToolbar/AdminToolbar.tsx @@ -0,0 +1,95 @@ +import React from 'react'; + +import { ContentPointer, getChangeRequest, getRevision, getRevisionPages } from '@/lib/api'; +import { tcls } from '@/lib/tailwind'; + +interface AdminToolbarProps { + content: ContentPointer; +} + +/** + * Toolbar with information for the content admin when previewing a revision or change-request. + */ +export function AdminToolbar(props: AdminToolbarProps) { + const { content } = props; + + return ( +
+ + {content.changeRequestId ? ( + + ) : null} + {content.revisionId ? ( + + ) : null} + +
+ ); +} + +async function ChangeRequestToolbar(props: { spaceId: string; changeRequestId: string }) { + const { spaceId, changeRequestId } = props; + + const changeRequest = await getChangeRequest(spaceId, changeRequestId); + + return ( + + Change request #{changeRequest.number}: {changeRequest.subject ?? 'No subject'} + + ); +} + +async function RevisionToolbar(props: { spaceId: string; revisionId: string }) { + const { spaceId, revisionId } = props; + + const revision = await getRevision(spaceId, revisionId); + + return ( + + Revision created on {new Date(revision.createdAt).toLocaleDateString()} + + ); +} + +function ToolbarButton(props: { href: string; children: React.ReactNode }) { + const { href, children } = props; + + return ( + + {children} + + ); +} diff --git a/src/components/AdminToolbar/index.ts b/src/components/AdminToolbar/index.ts new file mode 100644 index 000000000..bcbabf184 --- /dev/null +++ b/src/components/AdminToolbar/index.ts @@ -0,0 +1 @@ +export * from './AdminToolbar'; diff --git a/src/components/Search/SearchScopeToggle.tsx b/src/components/Search/SearchScopeToggle.tsx index fb75fe10c..0ff825ebf 100644 --- a/src/components/Search/SearchScopeToggle.tsx +++ b/src/components/Search/SearchScopeToggle.tsx @@ -18,7 +18,7 @@ export function SearchScopeToggle(props: { spaceTitle: string }) { return (
@@ -53,6 +53,8 @@ function ToggleButton(props: { onClick: () => void; children: React.ReactNode; a return (