Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/gitbook-v2/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
*/
const nextConfig = {
experimental: {
// This is needed to throw "forbidden" when the api token expired during revalidation
authInterrupts: true,

// This is needed to use 'use cache'
useCache: true,

// Content is fully static, we can cache it in the session memory cache for a long time
staleTimes: {
dynamic: 3600, // 1 hour
static: 3600, // 1 hour
},
},

env: {
Expand Down
2 changes: 2 additions & 0 deletions packages/gitbook-v2/src/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export async function getStaticSiteContext(params: RouteLayoutParams) {
const context = await fetchSiteContextByURLLookup(
getBaseContext({
siteURL,
siteURLData,
urlMode: getModeFromParams(params.mode),
}),
siteURLData
Expand All @@ -60,6 +61,7 @@ export async function getDynamicSiteContext(params: RouteLayoutParams) {
const context = await fetchSiteContextByURLLookup(
getBaseContext({
siteURL,
siteURLData,
urlMode: getModeFromParams(params.mode),
}),
siteURLData
Expand Down
4 changes: 3 additions & 1 deletion packages/gitbook-v2/src/app/~space/[spaceId]/pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ export async function getSpacePDFContext(

const apiToken = await getAPITokenFromMiddleware();

const basePath = getPDFRoutePath(params);
const linker = createLinker({
pathname: getPDFRoutePath(params),
spaceBasePath: basePath,
siteBasePath: basePath,
});
const dataFetcher = createDataFetcher({
apiToken: apiToken,
Expand Down
99 changes: 35 additions & 64 deletions packages/gitbook-v2/src/lib/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getSiteStructureSections } from '@/lib/sites';
import type {
ChangeRequest,
PublishedSiteContentLookup,
PublishedSiteContent,
RevisionPage,
RevisionPageDocument,
Site,
Expand All @@ -14,11 +14,10 @@ import type {
Space,
} from '@gitbook/api';
import { type GitBookDataFetcher, createDataFetcher, throwIfDataError } from '@v2/lib/data';
import { redirect } from 'next/navigation';
import { assert } from 'ts-essentials';
import { GITBOOK_URL } from './env';
import { type ImageResizer, createImageResizer } from './images';
import { type GitBookSpaceLinker, createLinker } from './links';
import { type GitBookLinker, createLinker } from './links';

/**
* Generic context when rendering content.
Expand All @@ -32,7 +31,7 @@ export type GitBookBaseContext = {
/**
* Linker to generate links in the current space.
*/
linker: GitBookSpaceLinker;
linker: GitBookLinker;

/**
* Image resizer to resize images.
Expand Down Expand Up @@ -102,59 +101,33 @@ export type GitBookPageContext = (GitBookSpaceContext | GitBookSiteContext) & {
};

/**
* Get the base context for a request.
* Get the base context for a request on a site.
*/
export function getBaseContext(input: {
siteURL: URL | string;
siteURLData: PublishedSiteContent;
urlMode: 'url' | 'url-host';
apiToken?: string | null;
}) {
const url = typeof input.siteURL === 'string' ? new URL(input.siteURL) : input.siteURL;
const urlMode = input.urlMode;
const { urlMode, siteURLData } = input;
const siteURL = typeof input.siteURL === 'string' ? new URL(input.siteURL) : input.siteURL;

const dataFetcher = createDataFetcher({
apiToken: input.apiToken ?? null,
apiToken: siteURLData.apiToken ?? null,
});

const linker = getLinkerForSiteURL({
siteURL: url,
urlMode,
});

const imageResizer = createImageResizer({
host: url.host,
// To ensure image resizing work for proxied sites,
// we serve images from the root of the site.
linker: linker,
});

return {
dataFetcher,
linker,
imageResizer,
};
}

/**
* Get the linker for a given site URL.
*/
export function getLinkerForSiteURL(input: {
siteURL: URL;
urlMode: 'url' | 'url-host';
}) {
const { siteURL, urlMode } = input;

const gitbookURL = GITBOOK_URL ? new URL(GITBOOK_URL) : undefined;
const linker =
urlMode === 'url-host'
? createLinker({
host: siteURL.host,
pathname: siteURL.pathname,
siteBasePath: siteURLData.siteBasePath,
spaceBasePath: siteURLData.basePath,
})
: createLinker({
protocol: gitbookURL?.protocol,
host: gitbookURL?.host,
pathname: `/url/${siteURL.host}${siteURL.pathname}`,
siteBasePath: `/url/${siteURL.host}${siteURLData.siteBasePath}`,
spaceBasePath: `/url/${siteURL.host}${siteURLData.basePath}`,
});

if (urlMode === 'url') {
Expand All @@ -165,39 +138,37 @@ export function getLinkerForSiteURL(input: {
};
}

return linker;
const imageResizer = createImageResizer({
host: siteURL.host,
// To ensure image resizing work for proxied sites,
// we serve images from the root of the site.
linker: linker,
});

return {
dataFetcher,
linker,
imageResizer,
};
}

/**
* Fetch the context of a site using the resolution of a URL
*/
export async function fetchSiteContextByURLLookup(
baseContext: GitBookBaseContext,
data: PublishedSiteContentLookup
data: PublishedSiteContent
): Promise<GitBookSiteContext> {
const { dataFetcher } = baseContext;
if ('redirect' in data) {
redirect(data.redirect);
}

return await fetchSiteContextByIds(
{
...baseContext,
dataFetcher: dataFetcher.withToken({
apiToken: data.apiToken,
}),
},
{
organization: data.organization,
site: data.site,
siteSection: data.siteSection,
siteSpace: data.siteSpace,
space: data.space,
shareKey: data.shareKey,
changeRequest: data.changeRequest,
revision: data.revision,
}
);
return await fetchSiteContextByIds(baseContext, {
organization: data.organization,
site: data.site,
siteSection: data.siteSection,
siteSpace: data.siteSpace,
space: data.space,
shareKey: data.shareKey,
changeRequest: data.changeRequest,
revision: data.revision,
});
}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/gitbook-v2/src/lib/images/createImageResizer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'server-only';

import { GITBOOK_IMAGE_RESIZE_SIGNING_KEY, GITBOOK_IMAGE_RESIZE_URL } from '../env';
import type { GitBookSpaceLinker } from '../links';
import type { GitBookLinker } from '../links';
import { type SignatureVersion, generateImageSignature } from './signatures';
import type { ImageResizer } from './types';

Expand Down Expand Up @@ -37,7 +37,7 @@ export function createImageResizer({
linker,
}: {
/** The linker to use to create URLs. */
linker: GitBookSpaceLinker;
linker: GitBookLinker;
/** The host name of the current site. */
host: string;
}): ImageResizer {
Expand All @@ -62,7 +62,7 @@ export function createImageResizer({
url: urlInput,
});

const url = linker.toAbsoluteURL(linker.toPathInContent('/~gitbook/image'));
const url = linker.toAbsoluteURL(linker.toPathInSite('/~gitbook/image'));
const searchParams = new URLSearchParams();
searchParams.set('url', getImageAPIUrl(urlInput));

Expand Down
59 changes: 32 additions & 27 deletions packages/gitbook-v2/src/lib/links.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import { describe, expect, it } from 'bun:test';
import { appendBasePathToLinker, createLinker } from './links';
import { createLinker } from './links';

const root = createLinker({
host: 'docs.company.com',
pathname: '/',
spaceBasePath: '/',
siteBasePath: '/',
});

const variantInSection = createLinker({
host: 'docs.company.com',
pathname: '/section/variant',
spaceBasePath: '/section/variant',
siteBasePath: '/',
});

const siteGitBookIO = createLinker({
host: 'org.gitbook.io',
spaceBasePath: '/sitename/variant/',
siteBasePath: '/sitename/',
});

describe('toPathInContent', () => {
it('should return the correct path', () => {
expect(root.toPathInContent('some/path')).toBe('/some/path');
expect(variantInSection.toPathInContent('some/path')).toBe('/section/variant/some/path');
expect(root.toPathInSpace('some/path')).toBe('/some/path');
expect(variantInSection.toPathInSpace('some/path')).toBe('/section/variant/some/path');
});

it('should handle leading slash', () => {
expect(root.toPathInContent('/some/path')).toBe('/some/path');
expect(variantInSection.toPathInContent('/some/path')).toBe('/section/variant/some/path');
expect(root.toPathInSpace('/some/path')).toBe('/some/path');
expect(variantInSection.toPathInSpace('/some/path')).toBe('/section/variant/some/path');
});
});

describe('toPathInSite', () => {
it('should return the correct path', () => {
expect(root.toPathInSite('some/path')).toBe('/some/path');
expect(siteGitBookIO.toPathInSite('some/path')).toBe('/sitename/some/path');
});
});

Expand All @@ -32,27 +47,17 @@ describe('toAbsoluteURL', () => {
});
});

describe('appendBasePathToLinker', () => {
const prefixedRoot = appendBasePathToLinker(root, '/section/variant');
const prefixedVariantInSection = appendBasePathToLinker(variantInSection, '/base');

describe('toPathInContent', () => {
it('should return the correct path', () => {
expect(prefixedRoot.toPathInContent('some/path')).toBe('/section/variant/some/path');
expect(prefixedVariantInSection.toPathInContent('some/path')).toBe(
'/section/variant/base/some/path'
);
});
describe('toLinkForContent', () => {
it('should return the correct path', () => {
expect(root.toLinkForContent('https://docs.company.com/some/path')).toBe('/some/path');
expect(siteGitBookIO.toLinkForContent('https://org.gitbook.io/sitename/some/path')).toBe(
'/sitename/some/path'
);
});

describe('toAbsoluteURL', () => {
it('should return the correct path', () => {
expect(prefixedRoot.toAbsoluteURL('some/path')).toBe(
'https://docs.company.com/some/path'
);
expect(prefixedVariantInSection.toAbsoluteURL('some/path')).toBe(
'https://docs.company.com/some/path'
);
});
it('should preserve an absolute URL if the site is not the same', () => {
expect(siteGitBookIO.toLinkForContent('https://org.gitbook.io/anothersite/some/path')).toBe(
'https://org.gitbook.io/anothersite/some/path'
);
});
});
Loading
Loading