Compare commits

..

8 Commits

Author SHA1 Message Date
Steven Hall e1fdce020f log 2024-04-04 16:22:59 +02:00
Scott Cazan 84e01f255e small typo 2024-04-04 15:52:37 +02:00
Steven H a007604ab1 Bump caches for documents. (#2265) 2024-04-04 14:09:43 +02:00
Steven H 3076e96061 Fix an issue where OpenAPI blocks could not be fetched. (#2267) 2024-04-04 12:28:30 +02:00
Steven H a3741dd65f Bump caches for revisions. (#2263) 2024-04-04 11:15:29 +02:00
Taran Vohra 07085a5b89 Set tracking prompt cookie age to 365days instead of a session cookie (#2266) 2024-04-04 14:39:27 +05:30
Samy Pessé 36600bc0c7 Fix styling for cards when no cover is defined (#2261) 2024-04-03 00:10:59 +02:00
Samy Pessé 5b7488b066 Handle ?theme query string to force theme in preview mode (#2260) 2024-04-02 13:55:27 +02:00
10 changed files with 86 additions and 15 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ Invalidate cache can be done at two levels using tags:
To invalidate and refetch the data cache, you can execute a POST request to `/~/gitbook/revalidate`:
```bash
curl --location --request POST 'https://gitbook/mycompany.com/~gitbook/revalidate' \
curl --location --request POST 'https://gitbook.mycompany.com/~gitbook/revalidate' \
--header 'Content-Type: application/json' \
--data-raw '{"tags": ["space.id"]}'
```
@@ -149,3 +149,37 @@ it('should parse Swagger 2.0', async () => {
},
});
});
it('should resolve a ref with whitespace', async () => {
const resolved = await fetchOpenAPIOperation(
{
url: ' https://petstore3.swagger.io/api/v3/openapi.json',
method: 'put',
path: '/pet',
},
fetcher,
);
expect(resolved).toMatchObject({
servers: [
{
url: '/api/v3',
},
],
operation: {
tags: ['pet'],
summary: 'Update an existing pet',
description: 'Update an existing pet by Id',
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'photoUrls'],
},
},
},
},
},
});
});
+20 -1
View File
@@ -1,5 +1,6 @@
import { CustomizationThemeMode } from '@gitbook/api';
import { Metadata, Viewport } from 'next';
import { headers } from 'next/headers';
import React from 'react';
import * as ReactDOM from 'react-dom';
@@ -47,7 +48,10 @@ export default async function ContentLayout(props: { children: React.ReactNode }
return (
<ClientContexts
nonce={nonce}
forcedTheme={customization.themes.toggeable ? undefined : customization.themes.default}
forcedTheme={
getQueryStringTheme() ??
(customization.themes.toggeable ? undefined : customization.themes.default)
}
>
<SpaceLayout
space={space}
@@ -124,3 +128,18 @@ export async function generateMetadata(): Promise<Metadata> {
robots: shouldIndexSpace({ space, collection }) ? 'index, follow' : 'noindex, nofollow',
};
}
/**
* For preview, the theme can be set via query string (?theme=light).
*/
function getQueryStringTheme() {
const headersList = headers();
const queryStringTheme = headersList.get('x-gitbook-theme');
if (!queryStringTheme) {
return null;
}
return queryStringTheme === 'light'
? CustomizationThemeMode.Light
: CustomizationThemeMode.Dark;
}
@@ -33,10 +33,7 @@ export async function RecordCard(
'z-0',
'relative',
'grid',
'grid-cols-[40%,_1fr]',
'bg-light',
'min-[432px]:grid-cols-none',
'min-[432px]:grid-rows-[auto,1fr]',
'w-[calc(100%+2px)]',
'h-[calc(100%+2px)]',
'inset-[-1px]',
@@ -44,6 +41,14 @@ export async function RecordCard(
'straight-corners:rounded-none',
'overflow-hidden',
'dark:bg-dark',
cover
? [
// On mobile, the cover is displayed on the left with 40% of the width
'grid-cols-[40%,_1fr]',
'min-[432px]:grid-cols-none',
'min-[432px]:grid-rows-[auto,1fr]',
]
: null,
)}
>
{cover ? (
+3 -1
View File
@@ -69,7 +69,9 @@ async function fetchVisitorID(): Promise<string> {
* Accept or reject cookies.
*/
export function setCookiesTracking(enabled: boolean) {
cookies.set(GRANTED_COOKIE, enabled ? 'yes' : 'no');
cookies.set(GRANTED_COOKIE, enabled ? 'yes' : 'no', {
expires: 365,
});
}
/**
+6 -6
View File
@@ -326,7 +326,7 @@ interface GetRevisionOptions {
* Get a revision by its ID.
*/
export const getRevision = cache(
'api.getRevision',
'api.getRevision.v2',
async (
spaceId: string,
revisionId: string,
@@ -359,7 +359,7 @@ export const getRevision = cache(
* Get all the pages in a revision of a space.
*/
export const getRevisionPages = cache(
'api.getRevisionPages.v3',
'api.getRevisionPages.v4',
async (
spaceId: string,
revisionId: string,
@@ -392,7 +392,7 @@ export const getRevisionPages = cache(
* Get a revision page by its path
*/
export const getRevisionPageByPath = cache(
'api.getRevisionPageByPath.v2',
'api.getRevisionPageByPath.v3',
async (
spaceId: string,
revisionId: string,
@@ -434,7 +434,7 @@ export const getRevisionPageByPath = cache(
* It should not be used directly, use `getRevisionFile` instead.
*/
const getRevisionFileById = cache(
'api.getRevisionFile.v2',
'api.getRevisionFile.v3',
async (spaceId: string, revisionId: string, fileId: string, options: CacheFunctionOptions) => {
try {
const response = await (async () => {
@@ -468,7 +468,7 @@ const getRevisionFileById = cache(
* It should not be used directly, use `getRevisionFile` instead.
*/
const getRevisionAllFiles = cache(
'api.getRevisionAllFiles',
'api.getRevisionAllFiles.v2',
async (spaceId: string, revisionId: string, options: CacheFunctionOptions) => {
const response = await getAll(
(params) =>
@@ -558,7 +558,7 @@ export const getRevisionFile = batch<[string, string, string], RevisionFile | nu
* Get a document by its ID.
*/
export const getDocument = cache(
'api.getDocument',
'api.getDocument.v2',
async (spaceId: string, documentId: string, options: CacheFunctionOptions) => {
const response = await api().spaces.getDocumentById(
spaceId,
+1
View File
@@ -126,6 +126,7 @@ export function cache<Args extends any[], Result>(
// Try the memory backend, independently of the other backends as it doesn't have a network cost
const memoryEntry = await memoryCache.get(key);
if (memoryEntry) {
console.log(`${key} memcache ${Date.now()} setAt=${memoryEntry.meta.setAt} expiresAt=${memoryEntry.meta.expiresAt} revalidatesAt=${memoryEntry.meta.revalidatesAt}`)
span.setAttribute('memory', true);
result = [memoryEntry, 'memory'] as const;
} else {
+1 -1
View File
@@ -185,7 +185,7 @@ export async function resizeImage(
return response;
}
return fetch(input, {
return fetch(parsed, {
// @ts-ignore
cf: {
image: resizeOptions,
+7 -2
View File
@@ -50,13 +50,18 @@ export async function fetchOpenAPIBlock(
const fetcher: OpenAPIFetcher = {
fetch: cache('openapi.fetch', async (url: string, options: CacheFunctionOptions) => {
const response = await fetch(url, {
// Wrap the raw string to prevent invalid URLs from being passed to fetch.
// This can happen if the URL has whitespace, which is currently handled differently by Cloudflare's implementation of fetch:
// https://github.com/cloudflare/workerd/issues/1957
const response = await fetch(new URL(url), {
...noCacheFetchOptions,
signal: options.signal,
});
if (!response.ok) {
throw new Error(`Failed to fetch OpenAPI file: ${response.statusText}`);
throw new Error(
`Failed to fetch OpenAPI file: ${response.status} ${response.statusText}`,
);
}
const text = await response.text();
+5
View File
@@ -196,6 +196,11 @@ export async function middleware(request: NextRequest) {
headers.set('x-gitbook-customization', customization);
}
const theme = url.searchParams.get('theme');
if (theme) {
headers.set('x-gitbook-theme', theme);
}
if (apiEndpoint) {
headers.set('x-gitbook-api', apiEndpoint);
}